core: add new enhanced object module
This commit is contained in:
@@ -9,7 +9,7 @@ set(CMAKE_C_EXTENSIONS OFF)
|
|||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
set(b_modules core ds serial term cmd io compress)
|
set(b_modules core ds object serial term cmd io compress)
|
||||||
|
|
||||||
set(b_system_name ${CMAKE_SYSTEM_NAME})
|
set(b_system_name ${CMAKE_SYSTEM_NAME})
|
||||||
string(TOLOWER ${b_system_name} b_system_name)
|
string(TOLOWER ${b_system_name} b_system_name)
|
||||||
|
|||||||
3
object/CMakeLists.txt
Normal file
3
object/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
include(../cmake/Templates.cmake)
|
||||||
|
|
||||||
|
add_bluelib_module(NAME object DEPENDENCIES core)
|
||||||
49
object/class.c
Normal file
49
object/class.c
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#include "class.h"
|
||||||
|
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
#include <blue/object/class.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void *z__b_class_get_interface(struct _b_class *c, const union b_type *id)
|
||||||
|
{
|
||||||
|
const struct b_type_registration *type_reg = c->c_type;
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_type_get_component(&type_reg->r_components, id);
|
||||||
|
|
||||||
|
if (!comp) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char *)c + comp->c_class_data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_result b_class_instantiate(
|
||||||
|
struct b_type_registration *type, struct _b_class **out_class)
|
||||||
|
{
|
||||||
|
struct _b_class *out = malloc(type->r_class_size);
|
||||||
|
if (!out) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(out, 0x0, type->r_class_size);
|
||||||
|
|
||||||
|
out->c_magic = B_CLASS_MAGIC;
|
||||||
|
out->c_type = type;
|
||||||
|
|
||||||
|
b_queue_iterator q_it;
|
||||||
|
b_queue_foreach (&q_it, &type->r_class_hierarchy) {
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_unbox(struct b_type_component, q_it.entry, c_entry);
|
||||||
|
const struct b_type_info *class_info = comp->c_type->r_info;
|
||||||
|
void *class_data = (char *)out + comp->c_class_data_offset;
|
||||||
|
|
||||||
|
if (class_info->t_class_init) {
|
||||||
|
class_info->t_class_init(out, class_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_class = out;
|
||||||
|
return B_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
18
object/class.h
Normal file
18
object/class.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef _CLASS_H_
|
||||||
|
#define _CLASS_H_
|
||||||
|
|
||||||
|
#include <blue/core/error.h>
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct b_type_registration;
|
||||||
|
|
||||||
|
struct _b_class {
|
||||||
|
uint64_t c_magic;
|
||||||
|
const struct b_type_registration *c_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern b_result b_class_instantiate(
|
||||||
|
struct b_type_registration *type, struct _b_class **out);
|
||||||
|
|
||||||
|
#endif
|
||||||
0
object/include/blue/object.h
Normal file
0
object/include/blue/object.h
Normal file
17
object/include/blue/object/class.h
Normal file
17
object/include/blue/object/class.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef BLUE_OBJECT_CLASS_H_
|
||||||
|
#define BLUE_OBJECT_CLASS_H_
|
||||||
|
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
|
||||||
|
#define B_CLASS_MAGIC 0xDEADFACEDCAFEBEDULL
|
||||||
|
#define B_CLASS(p) ((b_class *)(p))
|
||||||
|
|
||||||
|
typedef struct _b_class b_class;
|
||||||
|
|
||||||
|
#define b_class_get_interface(class, interface_struct, interface_id) \
|
||||||
|
((interface_struct *)z__b_class_get_interface( \
|
||||||
|
B_CLASS(class), interface_id));
|
||||||
|
|
||||||
|
BLUE_API void *z__b_class_get_interface(b_class *c, b_type id);
|
||||||
|
|
||||||
|
#endif
|
||||||
130
object/include/blue/object/macros.h
Normal file
130
object/include/blue/object/macros.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#ifndef BLUE_OBJECT_MACROS_H_
|
||||||
|
#define BLUE_OBJECT_MACROS_H_
|
||||||
|
|
||||||
|
#include <blue/core/thread.h>
|
||||||
|
#include <blue/object/class.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define __B_IFACE_I0(p, x) p##x
|
||||||
|
#define __B_IFACE_I1(p, x) __B_IFACE_I0(p, x)
|
||||||
|
|
||||||
|
/* Type definitions macros (for use in .c source file) */
|
||||||
|
|
||||||
|
#define B_TYPE_CLASS_DEFINITION_BEGIN(type_name) \
|
||||||
|
static void type_name##_class_init(b_class *p, void *d) \
|
||||||
|
{
|
||||||
|
#define B_TYPE_CLASS_DEFINITION_END(type_name) }
|
||||||
|
|
||||||
|
#define B_TYPE_CLASS_INTERFACE_BEGIN(interface_name, interface_id) \
|
||||||
|
interface_name##_class *__B_IFACE_I1(iface, __LINE__) \
|
||||||
|
= b_class_get_interface(p, interface_name##_class, interface_id); \
|
||||||
|
if (__B_IFACE_I1(iface, __LINE__)) { \
|
||||||
|
interface_name##_class *iface = __B_IFACE_I1(iface, __LINE__);
|
||||||
|
#define B_TYPE_CLASS_INTERFACE_END(interface_name, interface_id) }
|
||||||
|
#define B_INTERFACE_ENTRY(slot) iface->slot
|
||||||
|
|
||||||
|
#define B_TYPE_DEFINITION_BEGIN(name) \
|
||||||
|
static b_type_info name##_type_info = {0}; \
|
||||||
|
static void name##_class_init(b_class *, void *); \
|
||||||
|
static void name##_type_init(void) \
|
||||||
|
{ \
|
||||||
|
b_type_info *type_info = &name##_type_info; \
|
||||||
|
unsigned int nr_vtables = 0; \
|
||||||
|
type_info->t_name = #name; \
|
||||||
|
type_info->t_class_init = name##_class_init;
|
||||||
|
#define B_TYPE_DEFINITION_END(name) \
|
||||||
|
b_result result = b_type_register(type_info); \
|
||||||
|
if (b_result_is_error(result)) { \
|
||||||
|
b_throw_error_caused_by_error( \
|
||||||
|
B_ERRORS_BUILTIN, B_ERR_TYPE_REGISTRATION_FAILURE, \
|
||||||
|
result); \
|
||||||
|
abort(); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
b_type name##_get_type(void) \
|
||||||
|
{ \
|
||||||
|
static b_once static_type_init = B_ONCE_INIT; \
|
||||||
|
\
|
||||||
|
if (b_init_once(&static_type_init)) { \
|
||||||
|
name##_type_init(); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
return &name##_type_info.t_id; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define B_TYPE_ID(a, b, c, d, e) b_type_id_init(&type_info->t_id, a, b, c, d, e)
|
||||||
|
#define B_TYPE_EXTENDS(parent_id) \
|
||||||
|
b_type_id_copy(parent_id, &type_info->t_parent_id)
|
||||||
|
#define B_TYPE_IMPLEMENTS(interface_id) \
|
||||||
|
b_type_id_copy( \
|
||||||
|
interface_id, \
|
||||||
|
&type_info->t_interfaces[type_info->t_nr_interfaces++])
|
||||||
|
#define B_TYPE_CLASS(class_struct) \
|
||||||
|
type_info->t_class_size = sizeof(class_struct)
|
||||||
|
#define B_TYPE_INSTANCE_INIT(func) type_info->t_instance_init = (func)
|
||||||
|
#define B_TYPE_INSTANCE_FINI(func) type_info->t_instance_fini = (func)
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
#define B_TYPE_VTABLE_BEGIN(vtable_struct, interface_id) \
|
||||||
|
vtable_struct __B_IFACE_I1(iface, __LINE__) = {0}; \
|
||||||
|
{ \
|
||||||
|
vtable_struct *iface = &__B_IFACE_I1(iface, __LINE__); \
|
||||||
|
type_info->t_vtables[nr_vtables].v_vtable = iface; \
|
||||||
|
type_info->t_vtables[nr_vtables].v_interface_id = interface_id; \
|
||||||
|
nr_vtables++;
|
||||||
|
#define B_TYPE_VTABLE_END(vtable_struct, interface_id) }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define B_TYPE_INSTANCE_PRIVATE(instance_struct) \
|
||||||
|
type_info->t_instance_private_size = sizeof(instance_struct)
|
||||||
|
#define B_TYPE_INSTANCE_PROTECTED(instance_struct) \
|
||||||
|
type_info->t_instance_protected_size = sizeof(instance_struct)
|
||||||
|
|
||||||
|
/* Type declaration macros (for use in .h header file) */
|
||||||
|
|
||||||
|
#define B_DECLARE_TYPE(name) \
|
||||||
|
typedef struct _b_object name; \
|
||||||
|
typedef struct _##name##_class name##_class;
|
||||||
|
|
||||||
|
#define B_TYPE_CLASS_DECLARATION_BEGIN(name) struct _##name##_class {
|
||||||
|
#define B_TYPE_CLASS_DECLARATION_END(name) \
|
||||||
|
} \
|
||||||
|
;
|
||||||
|
|
||||||
|
#define B_TYPE_VIRTUAL_METHOD(return_type, method_name) \
|
||||||
|
return_type(*method_name)
|
||||||
|
|
||||||
|
#define B_TYPE_DEFAULT_CONSTRUCTOR(type_name, type_id) \
|
||||||
|
static inline type_name *type_name##_create(void) \
|
||||||
|
{ \
|
||||||
|
return b_object_create(type_id); \
|
||||||
|
}
|
||||||
|
#define B_TYPE_DEFAULT_DESTRUCTOR(type_name) \
|
||||||
|
static inline void type_name##_release(type_name *p) \
|
||||||
|
{ \
|
||||||
|
b_release(p); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Other macros */
|
||||||
|
|
||||||
|
#define B_CLASS_DISPATCH_VIRTUAL(type_name, type_id, object, func, ...) \
|
||||||
|
do { \
|
||||||
|
type_name##_class *iface = b_object_get_interface( \
|
||||||
|
object, type_name##_class, type_id); \
|
||||||
|
if (iface && iface->func) { \
|
||||||
|
return iface->func(__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
#define B_CLASS_DISPATCH_VIRTUAL_V(type_name, type_id, object, func, ...) \
|
||||||
|
do { \
|
||||||
|
type_name##_class *iface = b_object_get_interface( \
|
||||||
|
object, type_name##_class, type_id); \
|
||||||
|
if (iface && iface->func) { \
|
||||||
|
iface->func(__VA_ARGS__); \
|
||||||
|
return; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#endif
|
||||||
41
object/include/blue/object/object.h
Normal file
41
object/include/blue/object/object.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef BLUE_OBJECT_OBJECT_H_
|
||||||
|
#define BLUE_OBJECT_OBJECT_H_
|
||||||
|
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <blue/core/stringstream.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
|
||||||
|
#define B_OBJECT_MAGIC 0xDECAFC0C0ABEEF13ULL
|
||||||
|
|
||||||
|
#define B_OBJECT(p) ((b_object *)(p))
|
||||||
|
#define B_TYPE_OBJECT (b_object_get_type())
|
||||||
|
|
||||||
|
struct b_stream;
|
||||||
|
|
||||||
|
typedef struct _b_object b_object;
|
||||||
|
|
||||||
|
typedef struct _b_object_class {
|
||||||
|
void (*to_string)(b_object *, struct b_stream *);
|
||||||
|
} b_object_class;
|
||||||
|
|
||||||
|
#define b_object_get_private(object, private_struct, type_id) \
|
||||||
|
((private_struct *)z__b_object_get_private(B_OBJECT(object), type_id));
|
||||||
|
#define b_object_get_protected(object, protected_struct, type_id) \
|
||||||
|
((protected_struct *)z__b_object_get_protected(B_OBJECT(object), type_id));
|
||||||
|
#define b_object_get_interface(object, interface_struct, interface_id) \
|
||||||
|
((interface_struct *)z__b_object_get_interface( \
|
||||||
|
B_OBJECT(object), interface_id));
|
||||||
|
|
||||||
|
BLUE_API b_type b_object_get_type(void);
|
||||||
|
|
||||||
|
BLUE_API void *z__b_object_get_private(b_object *object, b_type type);
|
||||||
|
BLUE_API void *z__b_object_get_protected(b_object *object, b_type type);
|
||||||
|
BLUE_API void *z__b_object_get_interface(b_object *object, b_type type);
|
||||||
|
|
||||||
|
b_object *b_retain(b_object *p);
|
||||||
|
BLUE_API void b_release(b_object *p);
|
||||||
|
|
||||||
|
BLUE_API b_object *b_object_create(b_type type);
|
||||||
|
BLUE_API void b_object_to_string(b_object *p, struct b_stream *out);
|
||||||
|
|
||||||
|
#endif
|
||||||
78
object/include/blue/object/string.h
Normal file
78
object/include/blue/object/string.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#ifndef BLUE_OBJECT_STRING_H_
|
||||||
|
#define BLUE_OBJECT_STRING_H_
|
||||||
|
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <blue/object/macros.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
typedef enum b_strlen_flags {
|
||||||
|
B_STRLEN_NORMAL = 0,
|
||||||
|
B_STRLEN_IGNORE_ESC = 0x01u,
|
||||||
|
B_STRLEN_IGNORE_MOD = 0x02u,
|
||||||
|
} b_strlen_flags;
|
||||||
|
|
||||||
|
#define B_TYPE_STRING (b_string_get_type())
|
||||||
|
#define B_CSTR(s) (b_string_create_from_cstr(s))
|
||||||
|
#define B_STR_PTR(s) (b_string_ptr(s))
|
||||||
|
|
||||||
|
B_DECLARE_TYPE(b_string)
|
||||||
|
|
||||||
|
B_TYPE_CLASS_DECLARATION_BEGIN(b_string)
|
||||||
|
B_TYPE_CLASS_DECLARATION_END(b_string)
|
||||||
|
|
||||||
|
BLUE_API b_type b_string_get_type(void);
|
||||||
|
|
||||||
|
B_TYPE_DEFAULT_CONSTRUCTOR(b_string, B_TYPE_STRING);
|
||||||
|
B_TYPE_DEFAULT_DESTRUCTOR(b_string);
|
||||||
|
|
||||||
|
BLUE_API b_string *b_string_create_from_cstr(const char *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);
|
||||||
|
|
||||||
|
BLUE_API char *b_string_steal(b_string *str);
|
||||||
|
BLUE_API b_status b_string_reserve(b_string *str, size_t capacity);
|
||||||
|
BLUE_API b_status b_string_replace(
|
||||||
|
b_string *str, size_t start, size_t length, const char *new_data);
|
||||||
|
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));
|
||||||
|
static inline b_status b_string_toupper(b_string *str)
|
||||||
|
{
|
||||||
|
return b_string_transform(str, toupper);
|
||||||
|
}
|
||||||
|
static inline b_status b_string_tolower(b_string *str)
|
||||||
|
{
|
||||||
|
return b_string_transform(str, tolower);
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
b_string *dest, const char *src, size_t len, size_t at);
|
||||||
|
BLUE_API void b_string_insert_cstrf(
|
||||||
|
b_string *dest, size_t at, const char *format, ...);
|
||||||
|
BLUE_API void b_string_clear(b_string *str);
|
||||||
|
|
||||||
|
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 char b_string_front(const b_string *str);
|
||||||
|
BLUE_API char b_string_back(const b_string *str);
|
||||||
|
|
||||||
|
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 char *b_strdup(const char *s);
|
||||||
|
BLUE_API size_t b_strlen(const char *s, b_strlen_flags flags);
|
||||||
|
|
||||||
|
BLUE_API uint64_t b_cstr_hash(const char *s);
|
||||||
|
|
||||||
|
#endif
|
||||||
61
object/include/blue/object/type.h
Normal file
61
object/include/blue/object/type.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef BLUE_OBJECT_TYPE_H_
|
||||||
|
#define BLUE_OBJECT_TYPE_H_
|
||||||
|
|
||||||
|
#include <blue/core/error.h>
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define B_TYPE_MAX_INTERFACES 64
|
||||||
|
|
||||||
|
struct _b_class;
|
||||||
|
struct _b_object;
|
||||||
|
|
||||||
|
typedef void (*b_class_init_function)(struct _b_class *, void *);
|
||||||
|
typedef void (*b_instance_init_function)(struct _b_object *, void *);
|
||||||
|
typedef void (*b_instance_fini_function)(struct _b_object *, void *);
|
||||||
|
|
||||||
|
typedef const union b_type {
|
||||||
|
struct {
|
||||||
|
uint64_t p00, p01;
|
||||||
|
} a;
|
||||||
|
|
||||||
|
unsigned char b[16];
|
||||||
|
} *b_type;
|
||||||
|
|
||||||
|
typedef struct b_type_info {
|
||||||
|
union b_type t_id;
|
||||||
|
union b_type t_parent_id;
|
||||||
|
const char *t_name;
|
||||||
|
union b_type t_interfaces[B_TYPE_MAX_INTERFACES];
|
||||||
|
size_t t_nr_interfaces;
|
||||||
|
size_t t_class_size;
|
||||||
|
b_class_init_function t_class_init;
|
||||||
|
size_t t_instance_private_size;
|
||||||
|
size_t t_instance_protected_size;
|
||||||
|
b_instance_init_function t_instance_init;
|
||||||
|
b_instance_fini_function t_instance_fini;
|
||||||
|
} b_type_info;
|
||||||
|
|
||||||
|
BLUE_API void b_type_id_init(
|
||||||
|
union b_type *out, uint32_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||||
|
uint64_t e);
|
||||||
|
static inline void b_type_id_copy(b_type src, union b_type *dest)
|
||||||
|
{
|
||||||
|
dest->a.p00 = src->a.p00;
|
||||||
|
dest->a.p01 = src->a.p01;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int b_type_id_compare(b_type a, b_type b)
|
||||||
|
{
|
||||||
|
if (a == b) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return memcmp(a, b, sizeof(union b_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
BLUE_API b_result b_type_register(b_type_info *info);
|
||||||
|
|
||||||
|
#endif
|
||||||
146
object/object.c
Normal file
146
object/object.c
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
#include <blue/core/thread.h>
|
||||||
|
#include <blue/object/class.h>
|
||||||
|
#include <blue/object/macros.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
|
||||||
|
B_TYPE_CLASS_DEFINITION_BEGIN(b_object)
|
||||||
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
|
||||||
|
B_INTERFACE_ENTRY(to_string) = NULL;
|
||||||
|
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
|
||||||
|
B_TYPE_CLASS_DEFINITION_END(b_object)
|
||||||
|
|
||||||
|
B_TYPE_DEFINITION_BEGIN(b_object)
|
||||||
|
B_TYPE_ID(0x45f15a2c, 0x6831, 0x4bef, 0xb350, 0x15c650679211);
|
||||||
|
B_TYPE_CLASS(b_object_class);
|
||||||
|
B_TYPE_DEFINITION_END(b_object)
|
||||||
|
|
||||||
|
b_result b_object_instantiate(
|
||||||
|
struct b_type_registration *type, struct _b_object **out_object)
|
||||||
|
{
|
||||||
|
struct _b_object *out = malloc(type->r_instance_size);
|
||||||
|
if (!out) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(out, 0x0, type->r_instance_size);
|
||||||
|
|
||||||
|
out->obj_magic = B_OBJECT_MAGIC;
|
||||||
|
out->obj_type = type;
|
||||||
|
out->obj_ref = 1;
|
||||||
|
|
||||||
|
b_queue_iterator q_it;
|
||||||
|
b_queue_foreach (&q_it, &type->r_class_hierarchy) {
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_unbox(struct b_type_component, q_it.entry, c_entry);
|
||||||
|
const struct b_type_info *class_info = comp->c_type->r_info;
|
||||||
|
void *private_data
|
||||||
|
= (char *)out + comp->c_instance_private_data_offset;
|
||||||
|
|
||||||
|
if (class_info->t_instance_init) {
|
||||||
|
class_info->t_instance_init(out, private_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (comp->c_type == type) {
|
||||||
|
out->obj_main_priv_offset
|
||||||
|
= comp->c_instance_private_data_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_object = out;
|
||||||
|
return B_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _b_object *b_object_create(b_type type)
|
||||||
|
{
|
||||||
|
struct b_type_registration *type_reg = b_type_get_registration(type);
|
||||||
|
if (!type_reg) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _b_object *out = NULL;
|
||||||
|
b_result result = b_object_instantiate(type_reg, &out);
|
||||||
|
if (b_result_is_error(result)) {
|
||||||
|
b_error_release(result);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_object_to_string(struct _b_object *p, struct b_stream *out)
|
||||||
|
{
|
||||||
|
B_CLASS_DISPATCH_VIRTUAL_V(b_object, B_TYPE_OBJECT, p, to_string, p, out);
|
||||||
|
b_stream_write_fmt(out, NULL, "<%s@%p>", p->obj_type->r_info->t_name, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *z__b_object_get_private(struct _b_object *object, b_type type)
|
||||||
|
{
|
||||||
|
if (b_type_id_compare(&object->obj_type->r_info->t_id, type) == 0) {
|
||||||
|
return (char *)object + object->obj_main_priv_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_type_get_component(&object->obj_type->r_components, type);
|
||||||
|
if (!comp) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char *)object + comp->c_instance_private_data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *z__b_object_get_protected(struct _b_object *object, b_type type)
|
||||||
|
{
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_type_get_component(&object->obj_type->r_components, type);
|
||||||
|
if (!comp) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (char *)object + comp->c_instance_protected_data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *z__b_object_get_interface(struct _b_object *object, b_type type)
|
||||||
|
{
|
||||||
|
return z__b_class_get_interface(object->obj_type->r_class, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _b_object *b_retain(struct _b_object *p)
|
||||||
|
{
|
||||||
|
p->obj_ref++;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_release(struct _b_object *p)
|
||||||
|
{
|
||||||
|
if (p->obj_ref > 1) {
|
||||||
|
p->obj_ref--;
|
||||||
|
}
|
||||||
|
|
||||||
|
p->obj_ref = 0;
|
||||||
|
const struct b_type_registration *type = p->obj_type;
|
||||||
|
|
||||||
|
struct b_queue_entry *cur = b_queue_last(&type->r_class_hierarchy);
|
||||||
|
while (cur) {
|
||||||
|
struct b_type_component *comp
|
||||||
|
= b_unbox(struct b_type_component, cur, c_entry);
|
||||||
|
|
||||||
|
const struct b_type_info *class_info = comp->c_type->r_info;
|
||||||
|
void *private_data
|
||||||
|
= (char *)p + comp->c_instance_private_data_offset;
|
||||||
|
|
||||||
|
if (class_info->t_instance_fini) {
|
||||||
|
class_info->t_instance_fini(p, private_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = b_queue_prev(cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
p->obj_magic = 0;
|
||||||
|
p->obj_type = NULL;
|
||||||
|
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
19
object/object.h
Normal file
19
object/object.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#ifndef _OBJECT_H_
|
||||||
|
#define _OBJECT_H_
|
||||||
|
|
||||||
|
#include <blue/core/error.h>
|
||||||
|
#include <blue/core/misc.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct b_type_registration;
|
||||||
|
|
||||||
|
struct _b_object {
|
||||||
|
uint64_t obj_magic;
|
||||||
|
const struct b_type_registration *obj_type;
|
||||||
|
unsigned int obj_ref, obj_main_priv_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern b_result b_object_instantiate(
|
||||||
|
struct b_type_registration *type, struct _b_object **out);
|
||||||
|
|
||||||
|
#endif
|
||||||
773
object/string.c
Normal file
773
object/string.c
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
#include <blue/object/macros.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/string.h>
|
||||||
|
|
||||||
|
#define STRING_INLINE_CAPACITY 15
|
||||||
|
|
||||||
|
struct b_string_p {
|
||||||
|
/* length of string, not including null-terminator */
|
||||||
|
unsigned int s_len;
|
||||||
|
/* maximum length of string storable in the currently-allocated buffer, not including null terminator */
|
||||||
|
unsigned int s_max;
|
||||||
|
union {
|
||||||
|
char d_inline[STRING_INLINE_CAPACITY + 1];
|
||||||
|
char *d_external;
|
||||||
|
} s_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void string_init(b_string *p, void *priv)
|
||||||
|
{
|
||||||
|
struct b_string_p *str = priv;
|
||||||
|
|
||||||
|
str->s_len = 0;
|
||||||
|
str->s_max = STRING_INLINE_CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool string_is_inline(const struct b_string_p *str)
|
||||||
|
{
|
||||||
|
/* strings cannot go below STRING_INLINE_CAPACITY capacity */
|
||||||
|
return str->s_max == STRING_INLINE_CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *string_ptr(struct b_string_p *str)
|
||||||
|
{
|
||||||
|
if (string_is_inline(str)) {
|
||||||
|
return str->s_data.d_inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str->s_data.d_external;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_make_inline(struct b_string_p *str)
|
||||||
|
{
|
||||||
|
char *buffer = string_ptr(str);
|
||||||
|
memcpy(str->s_data.d_inline, buffer, sizeof str->s_data.d_inline);
|
||||||
|
str->s_data.d_inline[sizeof str->s_data.d_inline - 1] = '\0';
|
||||||
|
|
||||||
|
str->s_max = STRING_INLINE_CAPACITY;
|
||||||
|
|
||||||
|
if (str->s_len >= str->s_max) {
|
||||||
|
str->s_len = str->s_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_resize_large(struct b_string_p *str, size_t capacity)
|
||||||
|
{
|
||||||
|
char *buffer = string_ptr(str);
|
||||||
|
char *new_buffer = realloc(buffer, capacity + 1);
|
||||||
|
if (!new_buffer) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
str->s_max = capacity;
|
||||||
|
str->s_data.d_external = new_buffer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_make_large(struct b_string_p *str, size_t capacity)
|
||||||
|
{
|
||||||
|
const char *old_buffer = string_ptr(str);
|
||||||
|
char *buffer = malloc(capacity + 1);
|
||||||
|
if (!buffer) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buffer, old_buffer, sizeof str->s_data.d_inline);
|
||||||
|
buffer[str->s_len] = '\0';
|
||||||
|
|
||||||
|
str->s_max = capacity;
|
||||||
|
str->s_data.d_external = buffer;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int string_change_capacity(struct b_string_p *str, size_t capacity)
|
||||||
|
{
|
||||||
|
size_t old_capacity = str->s_max;
|
||||||
|
|
||||||
|
if (capacity < STRING_INLINE_CAPACITY) {
|
||||||
|
capacity = STRING_INLINE_CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool was_inline = string_is_inline(str);
|
||||||
|
bool is_now_inline = capacity == STRING_INLINE_CAPACITY;
|
||||||
|
|
||||||
|
if (capacity == old_capacity) {
|
||||||
|
/* this also handles the case where the old and new capacity both fit into the inline buffer. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!was_inline && is_now_inline) {
|
||||||
|
/* string was large, is now small enough to fit inline. */
|
||||||
|
return string_make_inline(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!was_inline) {
|
||||||
|
/* string was large, and is still large. */
|
||||||
|
return string_resize_large(str, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_now_inline) {
|
||||||
|
/* string was inline, and now large enough to require a buffer. */
|
||||||
|
return string_make_large(str, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nothing to do */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_string *b_string_create_from_cstr(const char *s)
|
||||||
|
{
|
||||||
|
b_string *p = b_string_create();
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
str->s_len = strlen(s);
|
||||||
|
string_change_capacity(str, str->s_len);
|
||||||
|
|
||||||
|
char *dest = string_ptr(str);
|
||||||
|
memcpy(dest, s, str->s_len);
|
||||||
|
dest[str->s_len] = 0;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_string *b_string_create_from_c(char c, size_t count)
|
||||||
|
{
|
||||||
|
b_string *p = b_string_create();
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!count || !c) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
string_change_capacity(str, count);
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
s[i] = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
str->s_len = count;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_string *b_string_duplicate(const b_string *p)
|
||||||
|
{
|
||||||
|
b_string *q = b_string_create();
|
||||||
|
if (!q) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
struct b_string_p *new_str
|
||||||
|
= b_object_get_private(q, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
string_change_capacity(new_str, str->s_len);
|
||||||
|
const char *src = string_ptr(str);
|
||||||
|
char *dst = string_ptr(new_str);
|
||||||
|
|
||||||
|
memcpy(dst, src, str->s_len);
|
||||||
|
new_str->s_len = str->s_len;
|
||||||
|
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *b_string_steal(b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
char *dest = NULL;
|
||||||
|
char *src = string_ptr(str);
|
||||||
|
|
||||||
|
if (string_is_inline(str)) {
|
||||||
|
dest = b_strdup(src);
|
||||||
|
src[0] = 0;
|
||||||
|
} else {
|
||||||
|
dest = src;
|
||||||
|
str->s_data.d_external = NULL;
|
||||||
|
str->s_max = STRING_INLINE_CAPACITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
str->s_len = 0;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_status string_reserve(struct b_string_p *str, size_t capacity)
|
||||||
|
{
|
||||||
|
if (str->s_max >= capacity) {
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = string_change_capacity(str, capacity);
|
||||||
|
|
||||||
|
return err == 0 ? B_SUCCESS : B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_string_reserve(b_string *p, size_t capacity)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
return string_reserve(str, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_string_replace(
|
||||||
|
b_string *p, size_t start, size_t length, const char *new_data)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
b_status status = B_SUCCESS;
|
||||||
|
size_t new_data_len = strlen(new_data);
|
||||||
|
|
||||||
|
if (start >= str->s_len) {
|
||||||
|
return B_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start + length >= str->s_len) {
|
||||||
|
length = str->s_len - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t new_str_len = str->s_len - length + new_data_len;
|
||||||
|
if (new_str_len > str->s_max) {
|
||||||
|
status = string_reserve(str, new_str_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!B_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
|
||||||
|
char *substitution_start = s + start;
|
||||||
|
char *excess_src = s + start + length;
|
||||||
|
size_t excess_length = str->s_len - start - length;
|
||||||
|
char *excess_dest = substitution_start + new_data_len;
|
||||||
|
|
||||||
|
memmove(excess_dest, excess_src, excess_length);
|
||||||
|
memmove(substitution_start, new_data, new_data_len);
|
||||||
|
s[new_str_len] = '\0';
|
||||||
|
|
||||||
|
str->s_len = new_str_len;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_string_replace_all(b_string *p, const char *new_data)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
size_t new_len = strlen(new_data);
|
||||||
|
string_reserve(str, new_len);
|
||||||
|
char *dest = string_ptr(str);
|
||||||
|
memcpy(dest, new_data, new_len);
|
||||||
|
dest[new_len] = '\0';
|
||||||
|
str->s_len = new_len;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_string_remove(b_string *p, size_t start, size_t length)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
b_status status = B_SUCCESS;
|
||||||
|
|
||||||
|
if (start >= str->s_len) {
|
||||||
|
return B_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start + length >= str->s_len) {
|
||||||
|
length = str->s_len - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t new_str_len = str->s_len - length;
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
|
||||||
|
char *removal_start = s + start;
|
||||||
|
char *excess_src = s + start + length;
|
||||||
|
size_t excess_length = str->s_len - start - length;
|
||||||
|
|
||||||
|
memmove(removal_start, excess_src, excess_length);
|
||||||
|
s[new_str_len] = '\0';
|
||||||
|
|
||||||
|
str->s_len = new_str_len;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_status b_string_transform(b_string *p, int (*transformer)(int))
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
for (size_t i = 0; i < str->s_len; i++) {
|
||||||
|
int c = transformer(s[i]);
|
||||||
|
|
||||||
|
if (c != 0) {
|
||||||
|
s[i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_close(struct b_stream *stream)
|
||||||
|
{
|
||||||
|
b_string *str = stream->s_ptr;
|
||||||
|
b_release(str);
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_getc(struct b_stream *stream, int *out)
|
||||||
|
{
|
||||||
|
b_string *p = stream->s_ptr;
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (stream->s_cursor >= str->s_len) {
|
||||||
|
return B_ERR_NO_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
*out = s[stream->s_cursor];
|
||||||
|
stream->s_cursor++;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_read(
|
||||||
|
struct b_stream *stream, unsigned char *buf, size_t count, size_t *nr_read)
|
||||||
|
{
|
||||||
|
b_string *p = stream->s_ptr;
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (stream->s_cursor >= str->s_len) {
|
||||||
|
*nr_read = 0;
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t available = str->s_len - stream->s_cursor;
|
||||||
|
size_t to_read = b_min(size_t, count, available);
|
||||||
|
|
||||||
|
char *s = string_ptr(str) + stream->s_cursor;
|
||||||
|
|
||||||
|
memcpy(buf, s, to_read);
|
||||||
|
|
||||||
|
*nr_read = to_read;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_write(
|
||||||
|
struct b_stream *stream, const unsigned char *buf, size_t count,
|
||||||
|
size_t *nr_written)
|
||||||
|
{
|
||||||
|
b_string *p = stream->s_ptr;
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
enum b_status status = B_SUCCESS;
|
||||||
|
|
||||||
|
if (stream->s_cursor + count > str->s_max) {
|
||||||
|
status = string_reserve(str, stream->s_cursor + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!B_OK(status)) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = string_ptr(str) + stream->s_cursor;
|
||||||
|
memcpy(s, buf, count);
|
||||||
|
s[str->s_max] = '\0';
|
||||||
|
stream->s_cursor += count;
|
||||||
|
str->s_len = b_max(size_t, str->s_len, stream->s_cursor + count);
|
||||||
|
|
||||||
|
*nr_written = count;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_seek(
|
||||||
|
struct b_stream *stream, long long offset, b_stream_seek_origin origin)
|
||||||
|
{
|
||||||
|
b_string *p = stream->s_ptr;
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
size_t abs_offset;
|
||||||
|
switch (origin) {
|
||||||
|
case B_STREAM_SEEK_START:
|
||||||
|
abs_offset = offset;
|
||||||
|
break;
|
||||||
|
case B_STREAM_SEEK_CURRENT:
|
||||||
|
abs_offset = stream->s_cursor + offset;
|
||||||
|
break;
|
||||||
|
case B_STREAM_SEEK_END:
|
||||||
|
abs_offset = str->s_len + offset;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return B_ERR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->s_cursor = abs_offset;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum b_status stream_reserve(struct b_stream *stream, size_t len)
|
||||||
|
{
|
||||||
|
b_string *p = stream->s_ptr;
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
size_t new_capacity = str->s_len + len;
|
||||||
|
return string_reserve(str, new_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum b_status b_string_open_stream(b_string *str, struct b_stream **out)
|
||||||
|
{
|
||||||
|
struct b_stream *stream = malloc(sizeof *stream);
|
||||||
|
if (!stream) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(stream, 0x0, sizeof *stream);
|
||||||
|
|
||||||
|
stream->s_mode |= B_STREAM_READ | B_STREAM_WRITE;
|
||||||
|
|
||||||
|
stream->s_ptr = b_retain(str);
|
||||||
|
stream->s_close = stream_close;
|
||||||
|
stream->s_getc = stream_getc;
|
||||||
|
stream->s_read = stream_read;
|
||||||
|
stream->s_write = stream_write;
|
||||||
|
stream->s_seek = stream_seek;
|
||||||
|
stream->s_reserve = stream_reserve;
|
||||||
|
|
||||||
|
*out = stream;
|
||||||
|
|
||||||
|
return B_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_insert(
|
||||||
|
struct b_string_p *dest, const char *src, size_t len, size_t at)
|
||||||
|
{
|
||||||
|
if (at >= dest->s_len) {
|
||||||
|
at = dest->s_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t new_size = dest->s_len + len;
|
||||||
|
if (dest->s_max < new_size) {
|
||||||
|
string_change_capacity(dest, new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dest_buf = string_ptr(dest);
|
||||||
|
char *from = dest_buf + at;
|
||||||
|
char *to = dest_buf + at + len;
|
||||||
|
|
||||||
|
memmove(to, from, dest->s_len - at);
|
||||||
|
memcpy(from, src, len);
|
||||||
|
dest_buf[new_size] = '\0';
|
||||||
|
|
||||||
|
dest->s_len = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_insertf(
|
||||||
|
struct b_string_p *dest, size_t at, const char *format, va_list arg)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
size_t len = vsnprintf(buf, sizeof buf, format, arg);
|
||||||
|
string_insert(dest, buf, len, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_insert_s(b_string *dest, const b_string *src, size_t at)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
struct b_string_p *src_str
|
||||||
|
= b_object_get_private(src, struct b_string_p, B_TYPE_STRING);
|
||||||
|
string_insert(dest_str, string_ptr(src_str), src_str->s_len, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_insert_cstr(b_string *dest, const char *src, size_t at)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
string_insert(dest_str, src, strlen(src), at);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_insert_cstrf(b_string *dest, size_t at, const char *format, ...)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
string_insertf(dest_str, at, format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_insert_cstrn(b_string *dest, const char *src, size_t len, size_t at)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
string_insert(dest_str, src, len, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_append_s(b_string *dest, const b_string *src)
|
||||||
|
{
|
||||||
|
b_string_insert_s(dest, src, SIZE_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_append_cstr(b_string *dest, const char *src)
|
||||||
|
{
|
||||||
|
b_string_insert_cstr(dest, src, SIZE_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_append_cstrf(b_string *dest, const char *format, ...)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
string_insertf(dest_str, SIZE_MAX, format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_prepend_s(b_string *dest, const b_string *src)
|
||||||
|
{
|
||||||
|
b_string_insert_s(dest, src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_prepend_cstr(b_string *dest, const char *src)
|
||||||
|
{
|
||||||
|
b_string_insert_cstr(dest, src, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_prepend_cstrf(b_string *dest, const char *format, ...)
|
||||||
|
{
|
||||||
|
struct b_string_p *dest_str
|
||||||
|
= b_object_get_private(dest, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, format);
|
||||||
|
string_insertf(dest_str, 0, format, arg);
|
||||||
|
va_end(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_clear(b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (str->s_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
*s = '\0';
|
||||||
|
str->s_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_string_get_size(const b_string *p, b_strlen_flags flags)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (flags != B_STRLEN_NORMAL) {
|
||||||
|
return b_strlen(string_ptr(str), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str->s_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_string_get_capacity(const b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
return str->s_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
char b_string_front(const b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (str->s_len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *s = string_ptr(str);
|
||||||
|
return s[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
char b_string_back(const b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (str->s_len == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *s = string_ptr(str);
|
||||||
|
return s[str->s_len - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_string_pop_back(b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (str->s_len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *s = string_ptr(str);
|
||||||
|
|
||||||
|
s[str->s_len - 1] = '\0';
|
||||||
|
str->s_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *b_string_ptr(const b_string *p)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (string_is_inline(str)) {
|
||||||
|
return str->s_data.d_inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str->s_data.d_external;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_string *b_string_substr(const b_string *p, size_t start, size_t len)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
|
||||||
|
if (start > str->s_len) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start + len > str->s_len) {
|
||||||
|
len = str->s_len - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_string *new_str = b_string_create();
|
||||||
|
struct b_string_p *new_str_p
|
||||||
|
= b_object_get_private(new_str, struct b_string_p, B_TYPE_STRING);
|
||||||
|
string_reserve(new_str_p, len);
|
||||||
|
|
||||||
|
const char *src = string_ptr(str) + start;
|
||||||
|
char *dest = string_ptr(new_str_p);
|
||||||
|
|
||||||
|
memcpy(dest, src, len);
|
||||||
|
new_str_p->s_len = len;
|
||||||
|
|
||||||
|
return new_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_fini(b_string *p, void *priv)
|
||||||
|
{
|
||||||
|
struct b_string_p *str = priv;
|
||||||
|
if (!string_is_inline(str)) {
|
||||||
|
free(string_ptr(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void string_to_string(b_string *p, struct b_stream *out)
|
||||||
|
{
|
||||||
|
struct b_string_p *str
|
||||||
|
= b_object_get_private(p, struct b_string_p, B_TYPE_STRING);
|
||||||
|
b_stream_write_string(out, string_ptr(str), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *b_strdup(const char *s)
|
||||||
|
{
|
||||||
|
size_t len = strlen(s);
|
||||||
|
char *p = malloc(len + 1);
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(p, s, len);
|
||||||
|
p[len] = '\0';
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t b_strlen(const char *s, b_strlen_flags flags)
|
||||||
|
{
|
||||||
|
if (!(flags & (B_STRLEN_IGNORE_ESC | B_STRLEN_IGNORE_MOD))) {
|
||||||
|
return strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t out = 0;
|
||||||
|
for (size_t i = 0; s[i]; i++) {
|
||||||
|
if (s[i] == '\033' && (flags & B_STRLEN_IGNORE_ESC)) {
|
||||||
|
while (!isalpha(s[i]) && s[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s[i] == '[' && (flags & B_STRLEN_IGNORE_MOD)) {
|
||||||
|
i++;
|
||||||
|
if (s[i] == '[') {
|
||||||
|
out++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (s[i] != ']' && s[i]) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
B_TYPE_CLASS_DEFINITION_BEGIN(b_string)
|
||||||
|
B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT)
|
||||||
|
B_INTERFACE_ENTRY(to_string) = string_to_string;
|
||||||
|
B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT)
|
||||||
|
B_TYPE_CLASS_DEFINITION_END(b_string)
|
||||||
|
|
||||||
|
B_TYPE_DEFINITION_BEGIN(b_string)
|
||||||
|
B_TYPE_ID(0xc986dd64, 0x7bfe, 0x4151, 0x8ffb, 0x251f4651fd35);
|
||||||
|
B_TYPE_CLASS(b_string_class);
|
||||||
|
B_TYPE_EXTENDS(B_TYPE_OBJECT);
|
||||||
|
B_TYPE_INSTANCE_PRIVATE(struct b_string_p);
|
||||||
|
B_TYPE_INSTANCE_INIT(string_init);
|
||||||
|
B_TYPE_DEFINITION_END(b_string)
|
||||||
309
object/type.c
Normal file
309
object/type.c
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
#include "type.h"
|
||||||
|
|
||||||
|
#include "class.h"
|
||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
#include <blue/core/btree.h>
|
||||||
|
#include <blue/core/endian.h>
|
||||||
|
#include <blue/object/object.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static struct b_btree type_list = B_BTREE_INIT;
|
||||||
|
static union b_type zero_id = {0};
|
||||||
|
|
||||||
|
struct type_init_ctx {
|
||||||
|
size_t ctx_class_offset;
|
||||||
|
size_t ctx_instance_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline int registration_compare(
|
||||||
|
const struct b_type_registration *a, const struct b_type_registration *b)
|
||||||
|
{
|
||||||
|
return b_type_id_compare(&a->r_info->t_id, &b->r_info->t_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int component_compare(
|
||||||
|
const struct b_type_component *a, const struct b_type_component *b)
|
||||||
|
{
|
||||||
|
return b_type_id_compare(&a->c_type->r_info->t_id, &b->c_type->r_info->t_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
B_BTREE_DEFINE_INSERT(
|
||||||
|
struct b_type_registration, r_node, r_info->r_id, put_type,
|
||||||
|
registration_compare)
|
||||||
|
B_BTREE_DEFINE_INSERT(
|
||||||
|
struct b_type_component, c_node, &c_type->r_info->t_id,
|
||||||
|
put_type_component, component_compare)
|
||||||
|
|
||||||
|
struct b_type_registration *get_type(const b_btree *tree, const union b_type *key)
|
||||||
|
{
|
||||||
|
b_btree_node *cur = tree->b_root;
|
||||||
|
while (cur) {
|
||||||
|
struct b_type_registration *cur_node
|
||||||
|
= b_unbox(struct b_type_registration, cur, r_node);
|
||||||
|
int cmp = b_type_id_compare(key, &cur_node->r_info->t_id);
|
||||||
|
|
||||||
|
if (cmp > 0) {
|
||||||
|
cur = b_btree_right(cur);
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
cur = b_btree_left(cur);
|
||||||
|
} else {
|
||||||
|
return cur_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_type_component *b_type_get_component(
|
||||||
|
const b_btree *tree, const union b_type *key)
|
||||||
|
{
|
||||||
|
b_btree_node *cur = tree->b_root;
|
||||||
|
while (cur) {
|
||||||
|
struct b_type_component *cur_node
|
||||||
|
= b_unbox(struct b_type_component, cur, c_node);
|
||||||
|
int cmp = b_type_id_compare(key, &cur_node->c_type->r_info->t_id);
|
||||||
|
|
||||||
|
if (cmp > 0) {
|
||||||
|
cur = b_btree_right(cur);
|
||||||
|
} else if (cmp < 0) {
|
||||||
|
cur = b_btree_left(cur);
|
||||||
|
} else {
|
||||||
|
return cur_node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct b_type_component *create_type_component(
|
||||||
|
const struct b_type_registration *type_reg)
|
||||||
|
{
|
||||||
|
struct b_type_component *c = malloc(sizeof *c);
|
||||||
|
if (!c) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(c, 0x0, sizeof *c);
|
||||||
|
|
||||||
|
c->c_type = type_reg;
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void b_type_id_init(
|
||||||
|
union b_type *out, uint32_t a, uint16_t b, uint16_t c, uint16_t d,
|
||||||
|
uint64_t e)
|
||||||
|
{
|
||||||
|
b_i32 x_a = b_i32_htob(a);
|
||||||
|
b_i16 x_b = b_i16_htob(b);
|
||||||
|
b_i16 x_c = b_i16_htob(c);
|
||||||
|
b_i16 x_d = b_i16_htob(d);
|
||||||
|
b_i64 x_e = b_i64_htob(e);
|
||||||
|
|
||||||
|
memcpy(&out->b[0], x_a.i_bytes, sizeof x_a.i_bytes);
|
||||||
|
memcpy(&out->b[4], x_b.i_bytes, sizeof x_b.i_bytes);
|
||||||
|
memcpy(&out->b[6], x_c.i_bytes, sizeof x_c.i_bytes);
|
||||||
|
memcpy(&out->b[8], x_d.i_bytes, sizeof x_d.i_bytes);
|
||||||
|
memcpy(&out->b[10], &x_e.i_bytes[2], sizeof x_e.i_bytes - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void initialise_type_component(
|
||||||
|
struct b_type_component *comp, const struct b_type_info *info,
|
||||||
|
struct type_init_ctx *init_ctx)
|
||||||
|
{
|
||||||
|
comp->c_class_data_offset = init_ctx->ctx_class_offset;
|
||||||
|
comp->c_class_data_size = info->t_class_size;
|
||||||
|
init_ctx->ctx_class_offset += comp->c_class_data_size;
|
||||||
|
|
||||||
|
comp->c_instance_private_data_offset = init_ctx->ctx_instance_offset;
|
||||||
|
comp->c_instance_private_data_size = info->t_instance_private_size;
|
||||||
|
init_ctx->ctx_instance_offset += comp->c_instance_private_data_size;
|
||||||
|
|
||||||
|
comp->c_instance_protected_data_offset = init_ctx->ctx_instance_offset;
|
||||||
|
comp->c_instance_protected_data_size = info->t_instance_protected_size;
|
||||||
|
init_ctx->ctx_instance_offset += comp->c_instance_protected_data_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_result locate_interface(
|
||||||
|
b_type interface_id, struct b_type_registration *dest,
|
||||||
|
struct type_init_ctx *init_ctx)
|
||||||
|
{
|
||||||
|
struct b_type_component *interface_comp
|
||||||
|
= b_type_get_component(&dest->r_components, interface_id);
|
||||||
|
if (interface_comp) {
|
||||||
|
return B_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_type_registration *interface_reg
|
||||||
|
= get_type(&type_list, interface_id);
|
||||||
|
if (!interface_reg) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_comp = create_type_component(interface_reg);
|
||||||
|
if (!interface_comp) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
put_type_component(&dest->r_components, interface_comp);
|
||||||
|
return B_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_result locate_interfaces(
|
||||||
|
const union b_type *interfaces, size_t nr_interfaces,
|
||||||
|
struct b_type_registration *dest, struct type_init_ctx *init_ctx)
|
||||||
|
{
|
||||||
|
b_result result = B_RESULT_SUCCESS;
|
||||||
|
for (size_t i = 0; i < nr_interfaces; i++) {
|
||||||
|
b_type interface_id = &interfaces[i];
|
||||||
|
result = locate_interface(interface_id, dest, init_ctx);
|
||||||
|
|
||||||
|
if (b_result_is_error(result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static b_result find_type_components(struct b_type_registration *reg)
|
||||||
|
{
|
||||||
|
const struct b_type_info *current = reg->r_info;
|
||||||
|
struct b_type_component *comp = create_type_component(reg);
|
||||||
|
if (!comp) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct type_init_ctx init_ctx = {
|
||||||
|
.ctx_instance_offset = sizeof(struct _b_object),
|
||||||
|
.ctx_class_offset = sizeof(struct _b_class),
|
||||||
|
};
|
||||||
|
|
||||||
|
put_type_component(®->r_components, comp);
|
||||||
|
b_queue_push_front(®->r_class_hierarchy, &comp->c_entry);
|
||||||
|
|
||||||
|
b_result result = locate_interfaces(
|
||||||
|
current->t_interfaces, current->t_nr_interfaces, reg, &init_ctx);
|
||||||
|
|
||||||
|
if (b_result_is_error(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_type current_id = ¤t->t_parent_id;
|
||||||
|
if (!current_id || b_type_id_compare(current_id, &zero_id) == 0) {
|
||||||
|
goto skip_class_hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
struct b_type_registration *dep_class
|
||||||
|
= get_type(&type_list, current_id);
|
||||||
|
if (!dep_class) {
|
||||||
|
return B_RESULT_ERR(NO_ENTRY);
|
||||||
|
}
|
||||||
|
|
||||||
|
comp = b_type_get_component(®->r_components, current_id);
|
||||||
|
if (comp) {
|
||||||
|
/* circular class dependency */
|
||||||
|
result = B_RESULT_ERR(INVALID_ARGUMENT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp = create_type_component(dep_class);
|
||||||
|
|
||||||
|
result = locate_interfaces(
|
||||||
|
dep_class->r_info->t_interfaces,
|
||||||
|
dep_class->r_info->t_nr_interfaces, reg, &init_ctx);
|
||||||
|
if (b_result_is_error(result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
put_type_component(®->r_components, comp);
|
||||||
|
b_queue_push_front(®->r_class_hierarchy, &comp->c_entry);
|
||||||
|
|
||||||
|
if (b_type_id_compare(current_id, B_TYPE_OBJECT) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b_queue_iterator q_it;
|
||||||
|
b_queue_foreach (&q_it, ®->r_class_hierarchy) {
|
||||||
|
comp = b_unbox(struct b_type_component, q_it.entry, c_entry);
|
||||||
|
initialise_type_component(comp, comp->c_type->r_info, &init_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
b_btree_iterator t_it;
|
||||||
|
b_btree_foreach (&t_it, ®->r_components) {
|
||||||
|
comp = b_unbox(struct b_type_component, t_it.node, c_node);
|
||||||
|
if (comp->c_type->r_category == B_TYPE_CLASS) {
|
||||||
|
/* this component was already initialised above */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialise_type_component(comp, comp->c_type->r_info, &init_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_class_hierarchy:
|
||||||
|
reg->r_instance_size = init_ctx.ctx_instance_offset;
|
||||||
|
reg->r_class_size = init_ctx.ctx_class_offset;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool type_has_base_class(struct b_type_info *info)
|
||||||
|
{
|
||||||
|
if (b_type_id_compare(&info->t_id, B_TYPE_OBJECT) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b_type_id_compare(&info->t_parent_id, &zero_id) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_result b_type_register(struct b_type_info *info)
|
||||||
|
{
|
||||||
|
if (!type_has_base_class(info)) {
|
||||||
|
b_type_id_copy(B_TYPE_OBJECT, &info->t_parent_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_type_registration *r = get_type(&type_list, &info->t_id);
|
||||||
|
if (r) {
|
||||||
|
return B_RESULT_ERR(NAME_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = malloc(sizeof *r);
|
||||||
|
if (!r) {
|
||||||
|
return B_RESULT_ERR(NO_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(r, 0x0, sizeof *r);
|
||||||
|
|
||||||
|
r->r_category = B_TYPE_CLASS;
|
||||||
|
r->r_info = info;
|
||||||
|
|
||||||
|
b_result result = find_type_components(r);
|
||||||
|
if (b_result_is_error(result)) {
|
||||||
|
free(r);
|
||||||
|
return b_result_propagate(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = b_class_instantiate(r, &r->r_class);
|
||||||
|
if (!r->r_class) {
|
||||||
|
free(r);
|
||||||
|
return b_error_with_msg_template_caused_by_error(
|
||||||
|
B_ERRORS_BUILTIN, B_ERR_TYPE_REGISTRATION_FAILURE,
|
||||||
|
result, B_MSG_TYPE_REGISTRATION_FAILURE,
|
||||||
|
B_ERROR_PARAM("typename", info->t_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
put_type(&type_list, r);
|
||||||
|
|
||||||
|
return B_RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_type_registration *b_type_get_registration(b_type id)
|
||||||
|
{
|
||||||
|
return get_type(&type_list, id);
|
||||||
|
}
|
||||||
39
object/type.h
Normal file
39
object/type.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef _TYPE_H_
|
||||||
|
#define _TYPE_H_
|
||||||
|
|
||||||
|
#include <blue/core/btree.h>
|
||||||
|
#include <blue/object/type.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
enum b_type_category {
|
||||||
|
B_TYPE_NONE = 0,
|
||||||
|
B_TYPE_CLASS,
|
||||||
|
B_TYPE_INTERFACE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_type_component {
|
||||||
|
struct b_btree_node c_node;
|
||||||
|
struct b_queue_entry c_entry;
|
||||||
|
const struct b_type_registration *c_type;
|
||||||
|
|
||||||
|
size_t c_class_data_offset, c_class_data_size;
|
||||||
|
size_t c_instance_private_data_offset, c_instance_private_data_size;
|
||||||
|
size_t c_instance_protected_data_offset, c_instance_protected_data_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_type_registration {
|
||||||
|
enum b_type_category r_category;
|
||||||
|
struct b_btree_node r_node;
|
||||||
|
const b_type_info *r_info;
|
||||||
|
struct _b_class *r_class;
|
||||||
|
struct b_btree r_components;
|
||||||
|
struct b_queue r_class_hierarchy;
|
||||||
|
|
||||||
|
size_t r_instance_size, r_class_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct b_type_registration *b_type_get_registration(b_type id);
|
||||||
|
extern struct b_type_component *b_type_get_component(
|
||||||
|
const b_btree *tree, const union b_type *key);
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user