Is it possible to dynamically define a struct in C - c

I'm pretty sure this will end up being a really obvious question, and that's why I haven't found much information on it. Still, I thought it was worth asking :)
Basically, accessing data using a struct is really fast. If data comes off the network in a form where it can be immediately processed as a struct, this is pretty sweet from a performance point of view.
However, is it possible to define a struct dynamically. Could a client and server app negotiate the format of the datastream and then use that definition as a struct?
If not, is there a better way of doing it?
Thanks all!

It isn't possible to dynamically define a struct that is identical to a compile-time struct.
It is possible, but difficult, to create dynamic structures that can contain the information equivalent to a struct. The access to the data is less convenient than what is available at compile-time.
All else apart, you cannot access a member somestruct.not_seen_at_compile_time using the dot . or arrow -> notation if it was not defined at compile-time.
With network communications, there are other issues to address - notably 'endianness'. That is, the data on the wire will probably include multi-byte (2, 4, 8) integers, and either the MSB or the LSB will be sent first, but if one machine is little-endian (IA-32, IA-64, x86/64) and the other is big-endian (SPARC, PPC, almost anything not from Intel), then the data will need to be transformed. Floating-point formats can also be problematic. There are numerous standards dedicated to defining how data will be sent across the network - it is not trivial. Some are specific: IP, TCP, UDP; others are general, such as ASN.1.
However, the 'cannot do dynamic data structures' part limits things - you have to agree beforehand on what the data structures are, and how they will be interpreted.
How do you do that?
gerty3000 asks:
It is possible, but difficult, to create dynamic structures that can contain the information equivalent to a struct. — How do you do that? I would like to pass dynamically-defined structs off to other C code (assume same compiler and other settings) without having to duplicate the struct memory layout routines from the compiler. I won't be accessing fields of these structs inside my process much (just initializing them once), so convenient syntax is not a concern.
You can't do it without duplicating the memory layout in some shape or form. It might not have to be exactly the same, but it is likely best if it is. Here's some sample code that shows roughly how it might be done.
dynstruct.c
This contains the basic structure manipulation material — structures to describe structures and (simple) members. Handling full arrays (as opposed to strings) would require more work, and there's a good deal of make-work replication to be managed for other types.
It also contains a main() program that tests the code. It makes a call to other_function(), which demonstrates that the structure I've defined in the data structures does match the structure exactly. The data does assume a 64-bit machine where double must be aligned on an 8-byte boundary (so there's a 4-byte hole in the structure); you will have to tweak the data for a machine where double can be on a 4-byte boundary.
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This is the type that will be simulated dynamically */
/*
struct simulated
{
int number;
double value;
char string[32];
};
*/
/* SOF structure.h */
typedef enum Type { INT, DOUBLE, STRING } Type;
typedef struct Descriptor
{
size_t offset;
Type type;
size_t type_size;
size_t array_dim;
char name[32];
} Descriptor;
typedef struct Structure
{
size_t size;
char name[32];
Descriptor *details;
} Structure;
extern void *allocate_structure(const Structure *structure);
extern void deallocate_structure(void *structure);
extern void *pointer_to_element(void *p, const Descriptor *d);
extern int get_int_element(void *p, const Descriptor *d);
extern void set_int_element(void *p, const Descriptor *d, int newval);
extern double get_double_element(void *p, const Descriptor *d);
extern void set_double_element(void *p, const Descriptor *d, double newval);
extern char *get_string_element(void *p, const Descriptor *d);
extern void set_string_element(void *p, const Descriptor *d, char *newval);
/* EOF structure.h */
static Descriptor details[] =
{
{ 0, INT, sizeof(int), 1, "number" },
{ 8, DOUBLE, sizeof(double), 1, "value" },
{ 16, STRING, sizeof(char), 32, "string" },
};
static Structure simulated = { 48, "simulated", details };
void *allocate_structure(const Structure *structure)
{
void *p = calloc(1, structure->size);
return p;
}
void deallocate_structure(void *structure)
{
free(structure);
}
void *pointer_to_element(void *p, const Descriptor *d)
{
void *data = (char *)p + d->offset;
return data;
}
int get_int_element(void *p, const Descriptor *d)
{
assert(d->type == INT);
int *v = pointer_to_element(p, d);
return *v;
}
void set_int_element(void *p, const Descriptor *d, int newval)
{
assert(d->type == INT);
int *v = pointer_to_element(p, d);
*v = newval;
}
double get_double_element(void *p, const Descriptor *d)
{
assert(d->type == DOUBLE);
double *v = pointer_to_element(p, d);
return *v;
}
void set_double_element(void *p, const Descriptor *d, double newval)
{
assert(d->type == DOUBLE);
double *v = pointer_to_element(p, d);
*v = newval;
}
char *get_string_element(void *p, const Descriptor *d)
{
assert(d->type == STRING);
char *v = pointer_to_element(p, d);
return v;
}
void set_string_element(void *p, const Descriptor *d, char *newval)
{
assert(d->type == STRING);
assert(d->array_dim > 1);
size_t len = strlen(newval);
if (len > d->array_dim)
len = d->array_dim - 1;
char *v = pointer_to_element(p, d);
memmove(v, newval, len);
v[len] = '\0';
}
extern void other_function(void *p);
int main(void)
{
void *sp = allocate_structure(&simulated);
if (sp != 0)
{
set_int_element(sp, &simulated.details[0], 37);
set_double_element(sp, &simulated.details[1], 3.14159);
set_string_element(sp, &simulated.details[2], "Absolute nonsense");
printf("Main (before):\n");
printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
printf("Double: %f\n", get_double_element(sp, &simulated.details[1]));
printf("String: %s\n", get_string_element(sp, &simulated.details[2]));
other_function(sp);
printf("Main (after):\n");
printf("Integer: %d\n", get_int_element(sp, &simulated.details[0]));
printf("Double: %f\n", get_double_element(sp, &simulated.details[1]));
printf("String: %s\n", get_string_element(sp, &simulated.details[2]));
deallocate_structure(sp);
}
return 0;
}
other.c
This code knows nothing about the structure description material in dynstruct.c; it knows about the struct simulated that the simulation code simulates. It prints the data it is passed and modifies it.
#include <stdio.h>
#include <string.h>
extern void other_function(void *p);
struct simulated
{
int number;
double value;
char string[32];
};
void other_function(void *p)
{
struct simulated *s = (struct simulated *)p;
printf("Other function:\n");
printf("Integer: %d\n", s->number);
printf("Double: %f\n", s->value);
printf("String: %s\n", s->string);
s->number *= 2;
s->value /= 2;
strcpy(s->string, "Codswallop");
}
Sample output
Main (before):
Integer: 37
Double: 3.141590
String: Absolute nonsense
Other function:
Integer: 37
Double: 3.141590
String: Absolute nonsense
Main (after):
Integer: 74
Double: 1.570795
String: Codswallop
Clearly, this code is not production-ready. It is a sufficient demonstration of what can be done. One issue you'd have to deal with is initializing the Structure and Descriptor data correctly. You can't put too many assertions into that sort of code. For example, I should really have assert(d->size == sizeof(double); in get_double_element(). It would also be sensible to include assert(d->offset % sizeof(double) == 0); to ensure that the double element is properly aligned. Or you might have a validate_structure(const Structure *sp); function that did all these validation checks. You'd need a function void dump_structure(FILE *fp, const char *tag, const Structure *sp); to dump the defined structure to the given file preceded by the tag, to assist in debugging. Etc.
This code is pure C; it is not compilable by a C++ compiler as C++. There aren't enough casts to satisfy a C++ compiler.

No, it isn't in C all data types must be known at compile time. That's what makes it "real fast".

Another theoretical possibility would be to compile some code at run-time using a compiler library such as libtcc.
While very appealing in theory (it does sound like a self-modifying application – your application would only have to generate C code for your struct and insert it in a template, then ask libtcc to compile it and then call some functions defined in your template to use that struct), this solution will probably not work great in practice. Why ? Well, as of 2016, libtcc (and the whole tcc project) is not very actively developed and there are issues with architectures such as x86_64.

For dynamic structure, the answer is no.
If you know what data comes in, in C++, you can use the overloaded << in operator to read the data from the stream..
In C, you could convert the stream to a string assuming that you know the length of the data comes in and using the function like sscanf, you could read the data.

You can't define a source-level struct, but you could do the same thing by setting up a data structure to store a name/tag and offset for each field of the data you want to communicate, and then store/read data at the right offsets according to that. Be sure you align all types to a boundary that's a multiple of sizeof(type) for portability. Of course, unless you're sure the client and server will have the same data representations (endianness and other considerations) and really need the performance of direct access, I would write proper serialize and deserialize routines instead...

Based on gerty3000's answer I made a library. I've abstracted something from the final user. It was hard buf finaly worked. If is there any improvement to do I am open for sugestions. Here it goes the code.
type-machine.h // define types and function prototipes
#ifndef TYPE_MACHINE_H
#define TYPE_MACHINE_H
#ifdef __cplusplus
extern "C" {
#endif
#define B8 char
#define B8U unsigned char
#define B16 short
#define B16U unsigned short
#define B32 int
#define B32U unsigned int
#define B64 long long int
#define B64U unsigned long long int
#define BP32 float
#define BP64 double
#define BIT_ON(var,bit) ((var)=((var) | (bit)))
#define BIT_OFF(var,bit) ((var)=((var) & (~bit)))
#define BIT_IS_ON(var,bit) (var & bit)
#define PAIR(position,value) ((value)=((position) << (1)))
typedef struct Bit8Tag BIT;
typedef enum {
Off, On
} STATUS;
typedef enum {
B8_T, B8U_T, B16_T, B16U_T, B32_T, B64_T, B64U_T, B32U_T, BP32_T, BP64_T
} TYPE;
typedef struct ClassFieldTag ClassField;
typedef struct ClassTag Class;
typedef enum {
CLASS_SIZE, CLASS_INSERT, CLASS_SHOW
} CLASS_MODE;
#if (defined(WIN32) || defined(WINDOWS_XP))
#define is_win()(1)
#else
#define is_win()(0)
#define TYPE_CALL
#define TYPE_TYPE
#endif // WIN32
#include <math.h>
#include <string.h>
#include <assert.h>
#define area(a,b) ((a)*(b))
#define radian(x,y)(atan2(y,x))
#define angle(a)( (a * (180 / M_PI)) + 180)
#if defined WIN32
#define ARIAL_PATH "C:/Windows/Fonts/arial.ttf\0"
#else
#define ARIAL_PATH "home/media/TheGreat/\0"
#endif
struct ClassFieldTag {
TYPE type;
size_t mem, size, len;
B8 name[32];
struct ClassFieldTag * next, *preview;
};
extern ClassField * class_set_push();
extern ClassField * class_field_set(ClassField * set, TYPE type, B8 * name, size_t len, size_t mem);
extern STATUS class_set_next_back(ClassField ** set, ClassField * next);
extern STATUS class_set_next_front(ClassField ** set, ClassField * next);
extern STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len);
extern STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len);
struct ClassTag {
B8 name[32];
void * data;
B8 * String;
B16 Short;
B16U UShort;
B32 Int;
B32U UInt;
B64 Long;
B64 ULong;
BP32 Float;
BP64 Double;
ClassField * field;
};
Class * class_push(B8 name[32]);
extern STATUS class_zero(Class * set, B8 name[32]);
extern void class_data_push(Class * set);
extern void class_data_pop(Class * set);
extern void * class_set_to(Class * set, ClassField * field);
extern void class_int_set(Class * set, ClassField * field, B32 value);
extern B32 class_int_get(Class * set, ClassField * field);
extern void class_double_set(Class * set, ClassField * field, BP64 value);
extern BP64 class_double_get(Class * set, ClassField * field);
extern void class_string_set(Class * set, ClassField * field, B8 * value);
extern B8 * class_string_get(Class * set, ClassField * field);
extern void class_mode(Class * set, ClassField * field, CLASS_MODE mode);
extern void class_field_pop(Class * set);
extern void class_pop(Class * set);
extern STATUS class_ex(Class * mine);
struct Bit8Tag {
unsigned b16 : 16;
};
extern void bit_on(BIT * value, int bit);
extern void bit_off(BIT * value, int bit);
extern STATUS bit_is_on(BIT value, int bit);
extern B32U strsub(B8 * data, B8 * key);
#ifdef __cplusplus
}
#endif
#endif // TYPE_MACHINE_H
type-machine.c // declares those functions
#include <Place/include/type-machine.h>
#include <malloc.h>
#include <stdio.h>
Class * class_push(B8 name[32]) {
Class * set = (Class *) malloc(sizeof (Class));
if(class_zero(set,name)){
return(set);
}
return(NULL);
}
void class_data_push(Class * set) {
B32 class_size = sizeof (Class), class_field_size = sizeof (ClassField);
if (set) {
if (class_size < sizeof (set))class_size = sizeof (set);
if (class_field_size < sizeof (set->field))class_field_size = sizeof (set->field);
}
set->data = malloc(class_size + class_field_size + 1);
}
void class_data_pop(Class * set) {
if (set && set->data) {
free(set->data);
}
}
void * class_set_to(Class * set, ClassField * field) {
if (set && set->data && field) {
void * data = (char *) set->data + field->mem;
return data;
}
return (NULL);
}
void class_int_set(Class * set, ClassField * field, B32 value) {
if (set) {
assert(field->type == B32_T);
B32 * update = class_set_to(set, field);
*update = value;
}
}
B32 class_int_get(Class * set, ClassField * field) {
if (set) {
assert(field->type == B32_T);
B32 * data = class_set_to(set, field);
return (*data);
}
return (0);
}
void class_double_set(Class * set, ClassField * field, BP64 value) {
if (set) {
assert(field->type == BP64_T);
BP64 * update = class_set_to(set, field);
*update = value;
}
}
BP64 class_double_get(Class * set, ClassField * field) {
if (set) {
assert(field->type == BP64_T);
BP64 * data = class_set_to(set, field);
return (*data);
}
return (0);
}
void class_string_set(Class * set, ClassField * field, B8 * value) {
if (set && field && field->len > 1 && value) {
assert(field->type == B8_T);
size_t len = strlen(value);
if (len < 2) {
len = 2;
}
if (len > field->len)len = field->len - 1;
B8 * buffer = class_set_to(set, field);
if (buffer) {
memmove(buffer, value, len);
buffer[len] = '\0';
}
}
}
B8 * class_string_get(Class * set, ClassField * field) {
if (set && field) {
assert(field->type == B8_T);
B8 * data = class_set_to(set, field);
return (data);
}
return (NULL);
}
STATUS class_zero(Class * set, B8 * name) {
if (set) {
set->String = NULL;
set->Short = 0;
set->UShort = 0;
set->Int = 0;
set->UInt = 0;
set->Long = 0;
set->ULong = 0;
set->Float = 0;
set->Double = 0;
set->data = NULL;
memset(set->name, 0, sizeof (set->name));
if (name)memmove(set->name, name, strlen(name));
set->field = NULL;
return (On);
}
return (Off);
}
ClassField * class_set_push() {
return (malloc(sizeof (ClassField)));
}
void class_field_pop(Class * set) {
if (set) {
ClassField * field = set->field;
while (field) {
ClassField * next = field->next;
if (field) {
free(field);
field = NULL;
}
field = next;
}
}
}
void class_pop(Class * set) {
if (set) {
class_data_pop(set);
class_field_pop(set);
free(set);
set = NULL;
}
}
ClassField * class_field_set(ClassField * field, TYPE type, B8 * name, size_t len, size_t mem) {
if (field) {
size_t lenght = (name) ? strlen(name) : 0;
if (lenght > 32) {
lenght = 31;
}
memcpy(field->name, name, lenght);
field->name[lenght] = 0;
field->type = type;
field->mem = mem;
field->len = len;
class_mode(NULL, field, CLASS_SIZE);
field->next = NULL;
field->preview = NULL;
return (field);
}
return (NULL);
}
STATUS class_set_next_back(ClassField ** field, ClassField * next) {
if (next == NULL)return (Off);
next->next = *field;
if (*field != NULL) {
(*field)->preview = next;
}
*field = next;
return (On);
}
STATUS class_set_next_front(ClassField ** field, ClassField * next) {
if (next == NULL)return (Off);
if (*field != NULL) {
ClassField * update = *field, *preview = NULL;
while (update->next != NULL) {
preview = update;
update = update->next;
}
update->preview = preview;
update->next = next;
return (On);
}
*field = next;
return (On);
}
STATUS class_insert_back(Class * set, TYPE type, B8 * name, size_t len) {
if (class_set_next_back(&set->field, class_field_set(class_set_push(), type, name, len, 0))) {
ClassField * preview = set->field;
if (preview->next) {
preview->mem = preview->next->mem + preview->next->size;
}
return (On);
}
return (Off);
}
STATUS class_insert_front(Class * set, TYPE type, B8 * name, size_t len) {
ClassField * next = class_field_set(class_set_push(), type, name, len, 0);
if (class_set_next_front(&set->field, next)) {
ClassField * preview = set->field;
while (preview) {
if (preview->next) {
if (preview->next == next) {
next->mem = preview->mem + preview->size;
}
}
preview = preview->next;
}
return (On);
}
return (Off);
}
void class_mode(Class * set, ClassField * field, CLASS_MODE mode) {
if (field) {
switch (field->type) {
case B8_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %s\n", field->name, class_string_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = field->len * sizeof (B8);
}
break;
case CLASS_INSERT:
{
class_string_set(set, field, set->String);
}
break;
}
}
break;
case B8U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %s\n", field->name, class_string_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = field->len * sizeof (B8U);
}
break;
case CLASS_INSERT:
{
class_string_set(set, field, set->String);
}
break;
}
}
break;
case B16_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B16);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B16U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B16U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B32_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: %i\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B32);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B32U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B32U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B64_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B64);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case B64U_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%i]\n", field->name, class_int_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (B64U);
}
break;
case CLASS_INSERT:
{
class_int_set(set, field, set->Int);
}
break;
}
}
break;
case BP32_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%lf]\n", field->name, class_double_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (BP32);
}
break;
case CLASS_INSERT:
{
class_double_set(set, field, set->Double);
}
break;
}
}
break;
case BP64_T:
{
switch (mode) {
case CLASS_SHOW:
{
printf("%s: [%lf]\n", field->name, class_double_get(set, field));
}
break;
case CLASS_SIZE:
{
field->size = sizeof (BP64);
}
break;
case CLASS_INSERT:
{
class_double_set(set, field, set->Double);
}
break;
}
}
break;
}
}
}
void bit_on(BIT * value, int bit) {
BIT_ON(value->b16, bit);
}
void bit_off(BIT * value, int bit) {
BIT_OFF(value->b16, bit);
}
STATUS bit_is_on(BIT value, int bit) {
if (value.b16 & bit)return (On);
return (Off);
}
B32U strsub(B8 * data, B8 * key) {
if (data && key) {
B8 *d = data;
B32U len = strlen(key), p = 0;
if (len > strlen(d))return (0);
while (*d != '\0') {
if (*(d + len) != '\0') {
B32U x = 0;
while (x <= len) {
if (key[x] == *d) {
*d++;
p++;
} else break;
x++;
}
if (x == len)return (p);
} else if (len == 1) {
if (*d == key[0])return (p);
}
p++;
*d++;
}
}
return (0);
}
main.c // testing....
#include "network.h"
#include <conio.h>
STATUS class_ex(Class * set) {
class_data_push(set);
if (set->data) {
ClassField * field = set->field;
while (field) {
if (!strcmp(field->name, "peso")) {
set->Double = 65.5;
}
if (!strcmp(field->name, "idade")) {
set->Int = 29;
}
if (!strcmp(field->name, "nome")) {
set->String = "Lisias de Castro Martins";
}
if (!strcmp(field->name, "endereco")) {
set->String = "Rua Mae D'Agua";
}
class_mode(set, field, CLASS_INSERT);
class_mode(set, field, CLASS_SHOW);
field = field->next;
}
return (On);
}
return (Off);
}
int main(int argc, char** argv) {
STATUS client_start = On;
if (client_start) {
Class * client = class_push("Client");;
class_insert_back(client, BP64_T, "peso", 1);
class_insert_back(client, B8_T, "endereco", 32);
class_insert_back(client, B32_T, "idade", 1);
class_insert_back(client, B8_T, "nome", 64);
printf("Classe[%s]\n\n", client->name);
if (class_ex(client)) {
}
class_pop(client);
getch();
}
return (EXIT_SUCCESS);
}
I still have to implement the short double and some other functions, but it is working.

Related

Will memory not freed cause segmentation fault in C?

I've just encountered a very strange bug. I was doing unit-test for a simple function as below.
UPDATE: Thanks #Bodo, here's the minimal working example. You can simply compile and run tokenizer.c.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
/* ============================= BOOL =============================== */
#ifndef _BOOL_
#define _BOOL_
typedef enum {
true, false
} bool;
#endif // _BOOL_
/* ============================= STACK =============================== */
#ifndef _STACK_
#define _STACK_
typedef void (*stack_freefn)(void *elemAddr);
typedef struct {
size_t size; // number of element allowed
int ite; // point to the current last element
size_t elemSize; // size of each element (how many bytes)
void *elems; // stockage of elements
stack_freefn freefn; // free memory allocated for each element if necessary
} stack;
/* constructor */
void new_stack(stack *s, const size_t size, const size_t elemSize, stack_freefn freefn) {
s->size = size;
s->ite = 0;
s->elemSize = elemSize;
s->elems = malloc(size * elemSize);
s->freefn = freefn;
}
/* free memory */
void dispose_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
}
free(s->elems);
s->elems = NULL;
}
/* push one new element on the top */
void push_stack(stack *s, const void *value, const size_t elemSize) {
if (s->ite == s->size) {
s->size *= 2;
s->elems = realloc(s->elems, s->size * s->elemSize);
}
void *elemAddr = (char *)s->elems + s->elemSize * s->ite++;
memcpy(elemAddr, value, s->elemSize);
}
/* pop our the element on the top */
void pop_stack(stack *s, void *res) {
if (s->ite > 0) {
void *elemAddr = (char *)s->elems + ((s->ite - 1) * s->elemSize);
memcpy(res, elemAddr, s->elemSize);
s->ite--;
}
}
void clear_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
} else {
s->ite = 0;
}
}
size_t stack_size(stack *s) {
return s->ite;
}
#endif // _STACK_
/* ============================= VECTOR =============================== */
#ifndef _VECTOR_
#define _VECTOR_
typedef int (*VectorCompareFunction)(const void *elemAddr1, const void *elemAddr2);
typedef void (*VectorFreeFunction)(void *elemAddr);
typedef struct {
int elemSize; //how many byte for each element
int elemNum; //number of current element in vector
int capacity; //maximum number of element vector can hold
void *elems; //pointer to data memory
VectorFreeFunction freefn; //pointer to the function used to free each element
} vector;
/**
* Reallocate a new memory of twice of original size
* return 1 if reallocation success, otherwise return -1.
*/
static void DoubleMemory(vector *v) {
void *tmp = realloc(v->elems, v->capacity * v->elemSize * 2);
assert(tmp != NULL);
v->elems = tmp;
v->capacity *= 2;
}
/**
* Constructor
*/
void VectorNew(vector *v, int elemSize, VectorFreeFunction freefn, int initialAllocation) {
v->elems = malloc(initialAllocation * elemSize);
assert(v->elems != NULL);
v->elemSize = elemSize;
v->elemNum = 0;
v->capacity = initialAllocation;
v->freefn = freefn;
}
/**
* Frees up all the memory of the specified vector.
*/
void VectorDispose(vector *v) {
if (v->freefn != NULL) {
for (; v->elemNum > 0; v->elemNum--) {
void *elemAddr = (char *)v->elems + (v->elemNum - 1) * v->elemSize;
v->freefn(elemAddr);
}
}
free(v->elems);
v->elems = NULL;
}
/**
* Returns the logical length of the vector.
*/
int VectorLength(const vector *v) {
return v->elemNum;
}
/**
* Appends a new element to the end of the specified vector.
*/
void VectorAppend(vector *v, const void *elemAddr) {
/* double size if neccessary */
if (v->elemNum == v->capacity) DoubleMemory(v);
memcpy((char *)v->elems + v->elemNum * v->elemSize, elemAddr, v->elemSize);
v->elemNum++;
}
/**
* Search the specified vector for an element whose contents match the element passed as the key.
*/
int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchfn, int startIndex, bool isSorted) {
assert(key && searchfn);
if (v->elemNum == 0) return -1;
assert(startIndex >= 0 && startIndex < v->elemNum);
if (isSorted == true) {
/* binary search */
void *startAddr = (char *)v->elems + startIndex * v->elemSize;
int size = v->elemNum - startIndex;
void *resAddr = bsearch(key, startAddr, size, v->elemSize, searchfn);
return (resAddr != NULL)? ((char *)resAddr - (char *)v->elems) / v->elemSize : -1;
} else {
/* linear search */
for (int i = 0; i < v->elemNum; i++) {
if (searchfn((char *)v->elems + i * v->elemSize, key) == 0) {
return i;
}
}
return -1;
}
}
#endif // _VECTOR_
/* ============================= TOKENIZER =============================== */
/**
* Dump current string into vector as a new word.
* Strings are null-terminated.
*/
static void dumpstack(stack *s, vector *v) {
size_t len = stack_size(s);
char *word = (char *)malloc((len + 1) * sizeof(char)); // +1 for null-terminator
for (int i = len - 1; i >= 0; i--) {
pop_stack(s, word + i * sizeof(char));
}
word[len] = '\0';
VectorAppend(v, &word);
clear_stack(s);
}
static const size_t kTokenStackDefaultSize = 64;
static void tokenize(vector *words, char *stream) {
stack s;
new_stack(&s, kTokenStackDefaultSize, sizeof(char), NULL);
size_t len = strlen(stream);
bool begin = false;
char c;
for (int i = 0; i < len; i++) {
c = stream[i];
/* =============================== My printf() is here ============================== */
// printf("char c = [%c]\n", c);
/* =============================== My printf() is here ============================== */
if (isalpha(c) || isdigit(c)) {
if (begin == false) begin = true;
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
} else if (c == '-') {
if (begin == true) { // case: covid-19
push_stack(&s, &c, sizeof(char));
} else {
if (i < len - 1 && isdigit(stream[i + 1])) { // case: -9
begin = true;
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
} else if (c == '.' && begin == true) { // case: 3.14
if (isdigit(stream[i - 1])) {
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
dispose_stack(&s);
}
/* ============================= UNIT-TEST =============================== */
/**
* HashSetFreeFunction<char *>
*/
static void freestr(void *elemAddr) {
char *str = *(char **)elemAddr;
free(str);
}
/**
* HashSetCompareFunction<char *>
*/
static int compstr(const void *elemAddr1, const void *elemAddr2) {
char *str1 = *(char **)elemAddr1;
char *str2 = *(char **)elemAddr2;
return strcmp(str1, str2);
}
static void test_tokenize(void) {
printf("Testing Tokenizer.c::tokenize() ...\n");
char *sentence = "Covid-19: Top adviser warns France at 'emergency' virus moment - BBC News\nPi = 3.14\n-1 is negative.";
vector words;
VectorNew(&words, sizeof(char *), freestr, 256);
tokenize(&words, sentence);
char *musthave[] = {"covid-19", "top", "3.14", "-1"};
char *musthavenot[] = {"-", "1"};
assert(VectorLength(&words) == 16);
for (int i = 0; i < sizeof(musthave)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthave[i], compstr, 0, false) != -1);
}
for (int i = 0; i < sizeof(musthavenot)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthavenot[i], compstr, 0, false) == -1);
}
VectorDispose(&words);
printf("[ALL PASS]\n");
}
int main(void) {
test_tokenize();
}
I've got segmentation fault at first.
[1] 4685 segmentation fault testtokenizer
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
Just recall that before this test, I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault? Thx bros.
UPDATE:
Now I can't even reproduce this bug myself. tokenizer.c above can pass the unit-test. I thought it might caused by makefile prerequisite rules. gcc didn't re-compile some object files when source code is changed.
Thanks #Steve Summit, you make it clear that unfreed memory will not cause segmentation fault.
Thanks #schwern for code review, it's really helpful.
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
They call it undefined behavior, because its behavior is undefined. Seemingly unrelated operations might nudge things just a bit to make the code "work" but they're only tangentially related to the problem.
I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault?
No. It does mean the memory is unreferencable and "leaked". The memory will be freed to the operating system when the process exits.
The problem must lie elsewhere. Without seeing your whole program we can't say for sure, but two fishy things stand out.
You're defining a fixed sized stack, but you're pushing onto it an indeterminate number of times. Unless push_stack has protection against this, you will walk off your allocated memory.
You're storing references to variables on the stack. lower, c
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
Once lower goes out of scope it will automatically be freed and the memory reused. &lower is invalid once tokenize returns. This seems to be fine if your stack only lasts the length of the function, but it's worth noting.
And it's possible new_stack, push_stack, or dumpstack are doing something incorrect.

C: Multi producer, multi consumer bounded queue

I try (better tried) to implement a circular buffer with the following interface:
ring_buffer *ring_buffer_create(int capacity, int element_size);
void ring_buffer_destroy(ring_buffer *buffer)
const void *ring_buffer_read_acquire(ring_buffer *buffer, ring_buffer_loc *loc);
void ring_buffer_read_finish(ring_buffer *buffer, ring_buffer_loc loc);
void *ring_buffer_write_acquire(ring_buffer *buffer, ring_buffer_loc *loc);
void ring_buffer_write_finish(ring_buffer *buffer, ring_buffer_loc loc);
It should be possible to read / write multiple elements concurrently (and even in parallel). E.g.:
ring_buffer *buffer = ring_buffer_create(10, sizeof(int));
/* Write a single element */
ring_buffer_loc loc0;
int *i0 = ring_buffer_write_acquire(buffer, &loc);
*i0 = 42; // this could be a big data structure and way more expensive
ring_buffer_write_finish(buffer, loc0);
/* Write "concurrently" */
ring_buffer_loc loc1, loc2;
int *i1 = ring_buffer_write_acquire(buffer, &loc);
int *i2 = ring_buffer_write_acquire(buffer, &loc);
*i1 = 1729;
*i2 = 314;
ring_buffer_write_finish(buffer, loc1);
ring_buffer_write_finish(buffer, loc2);
All "acquire"-functions should be blocking until the operation is possible.
So far, so good. I thought this is simple and so I started with a clean implementation which is based on mutex. But soon I could see that this was far too slow for my use-case (100'000 writes and reads per second), so I switched over to spin-locks etc.
My implementation became quite messy and at some point (now), I started to think about why not something "simple" like this with the desired interface already exists? Probably, it is anyway not a great idea to re-implement something like this.
Maybe someone knows an implementation which has such an interface and which is blocking if the operation is not possible? I was looking quite long in the internet, but I could not find a good match for my problem. Maybe my desired interface is just "bad" or "wrong"?
Nevertheless, I add my current code. It basically assigns each "cell" (=value) a state which can be NONE (not set; the cell is basically empty), WRITING (someone acquired the cell to write data), READING (someone acquired the cell to read) and SET (the cell has a value which could be read). Each cell has a spin-lock which is used to update the cell state.
It then works like this:
When someone acquires a read and the current cell has the state "SET", then the value can be read (new state is READING) and the read index is increased. In all other cases a conditional variable is used to wait until an element is available. When an element read is finished, the cell state is changed to NONE and if any writers are waiting, a conditional variable signal is sent.
The same is true if a cell write is acquires. The only difference is that the cell needs the state "NONE" to be used and possible readers are signaled if there are any.
For some reasons the code sometimes locks and so I had to add a "dirty" timeout to my conditional variable. I would already be super happy if this could be solved, because the "timeout" basically makes the code polling (which is relatively ugly) and at the same time many context switches are done. Maybe someone sees the bug? The "new" code also has the disadvantage that it sometimes is really slow which is like a killer for my application. I attached the "old" and the "new" code (the changed lines are marked).
Thank you for helping me:)!
#include <stdio.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
typedef int ring_buffer_loc;
enum t_ring_buffer_cell_state
{
NONE = 0,
WRITING = 1,
READING = 2,
SET = 3
};
typedef struct {
char *buffer; // data
atomic_int_fast8_t *states; // state per cell
pthread_spinlock_t *locks; // lock per cell
int capacity;
int element_size;
pthread_spinlock_t read_i_lock;
int read_i;
pthread_spinlock_t write_i_lock;
int write_i;
pthread_spinlock_t waiting_readers_lock;
int waiting_readers;
pthread_spinlock_t waiting_writers_lock;
int waiting_writers;
pthread_mutex_t value_written_lock;
pthread_mutex_t value_read_lock;
pthread_cond_t value_written;
pthread_cond_t value_read;
} ring_buffer;
ring_buffer *ring_buffer_create(int capacity, int element_size)
{
ring_buffer *res = calloc(1, sizeof(ring_buffer));
res->buffer = calloc(capacity, element_size);
res->states = calloc(capacity, sizeof(*res->states));
res->locks = malloc(capacity * sizeof(*res->locks));
for (int i = 0; i < capacity; ++i) {
pthread_spin_init(&res->locks[i], PTHREAD_PROCESS_PRIVATE);
}
pthread_spin_init(&res->write_i_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->read_i_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->waiting_readers_lock, PTHREAD_PROCESS_PRIVATE);
pthread_spin_init(&res->waiting_writers_lock, PTHREAD_PROCESS_PRIVATE);
res->capacity = capacity;
res->element_size = element_size;
return res;
}
void ring_buffer_destroy(ring_buffer *buffer)
{
free(buffer->buffer);
free(buffer->states);
free(buffer);
}
static inline void ring_buffer_inc_index(ring_buffer *buffer, int *index)
{
*index = (*index + 1) % buffer->capacity;
}
void timespec_now_plus_ms(struct timespec *result, long ms_to_add)
{
const int one_second_us = 1000 * 1000 * 1000;
timespec_get(result, TIME_UTC);
const long nsec = result->tv_nsec + ms_to_add * 1000 * 1000;
result->tv_sec += nsec / one_second_us;
result->tv_nsec += nsec % one_second_us;
}
const void *ring_buffer_read_acquire(ring_buffer *buffer, ring_buffer_loc *loc)
{
bool is_waiting = false;
start:
pthread_spin_lock(&buffer->read_i_lock);
const int read_i = buffer->read_i;
pthread_spinlock_t *cell_lock = &buffer->locks[read_i];
pthread_spin_lock(cell_lock);
const int state = buffer->states[read_i];
if (state == NONE || state == WRITING || state == READING) {
if (!is_waiting) {
is_waiting = true;
pthread_spin_lock(&buffer->waiting_readers_lock);
++buffer->waiting_readers;
pthread_mutex_lock(&buffer->value_written_lock);
pthread_spin_unlock(&buffer->waiting_readers_lock);
} else {
pthread_mutex_lock(&buffer->value_written_lock);
}
pthread_spin_unlock(cell_lock);
pthread_spin_unlock(&buffer->read_i_lock);
// "new" code:
// struct timespec ts;
// do {
// timespec_now_plus_ms(&ts, 50);
// } while (pthread_cond_timedwait(&buffer->value_written, &buffer->value_written_lock, &ts) == ETIMEDOUT && buffer->states[read_i] == state);
// pthread_mutex_unlock(&buffer->value_written_lock);
// "old" code (which hangs quite often):
pthread_cond_wait(&buffer->value_written, &buffer->value_written_lock);
pthread_mutex_unlock(&buffer->value_written_lock);
goto start;
} else if (state == SET) {
if (is_waiting) {
pthread_spin_lock(&buffer->waiting_readers_lock);
--buffer->waiting_readers;
assert(buffer->waiting_readers >= 0);
pthread_spin_unlock(&buffer->waiting_readers_lock);
}
buffer->states[read_i] = READING;
ring_buffer_inc_index(buffer, &buffer->read_i);
pthread_spin_unlock(&buffer->read_i_lock);
pthread_spin_unlock(cell_lock);
*loc = read_i;
return &buffer->buffer[read_i * buffer->element_size];
} else {
printf("unknown state!\n");
exit(1);
}
}
void ring_buffer_read_finish(ring_buffer *buffer, ring_buffer_loc loc)
{
pthread_spinlock_t *cell_lock = &buffer->locks[loc];
pthread_spin_lock(cell_lock);
buffer->states[loc] = NONE;
pthread_spin_unlock(cell_lock);
pthread_spin_lock(&buffer->waiting_writers_lock);
if (buffer->waiting_writers > 0) {
pthread_cond_signal(&buffer->value_read);
}
pthread_spin_unlock(&buffer->waiting_writers_lock);
}
void *ring_buffer_write_acquire(ring_buffer *buffer, ring_buffer_loc *loc)
{
bool is_waiting = false;
start:
pthread_spin_lock(&buffer->write_i_lock);
const int write_i = buffer->write_i;
pthread_spinlock_t *cell_lock = &buffer->locks[write_i];
pthread_spin_lock(cell_lock);
const int state = buffer->states[write_i];
if (state == SET || state == READING || state == WRITING) {
if (!is_waiting) {
is_waiting = true;
pthread_spin_lock(&buffer->waiting_writers_lock);
++buffer->waiting_writers;
pthread_mutex_lock(&buffer->value_read_lock);
pthread_spin_unlock(&buffer->waiting_writers_lock);
} else {
pthread_mutex_lock(&buffer->value_read_lock);
}
pthread_spin_unlock(cell_lock);
pthread_spin_unlock(&buffer->write_i_lock);
// "new" code:
// struct timespec ts;
// do {
// timespec_now_plus_ms(&ts, 5);
// } while (pthread_cond_timedwait(&buffer->value_read, &buffer->value_read_lock, &ts) == ETIMEDOUT && buffer->states[write_i] == state);
// pthread_mutex_unlock(&buffer->value_read_lock);
// "old" code (which hangs quite often):
pthread_cond_wait(&buffer->value_read, &buffer->value_read_lock);
pthread_mutex_unlock(&buffer->value_read_lock);
goto start;
} else if (state == NONE) {
if (is_waiting) {
pthread_spin_lock(&buffer->waiting_writers_lock);
--buffer->waiting_writers;
assert(buffer->waiting_writers >= 0);
pthread_spin_unlock(&buffer->waiting_writers_lock);
}
buffer->states[write_i] = WRITING;
ring_buffer_inc_index(buffer, &buffer->write_i);
pthread_spin_unlock(&buffer->write_i_lock);
pthread_spin_unlock(cell_lock);
*loc = write_i;
return &buffer->buffer[write_i * buffer->element_size];
} else {
printf("unknown state!\n");
exit(1);
}
}
void ring_buffer_write_finish(ring_buffer *buffer, ring_buffer_loc loc)
{
pthread_spinlock_t *cell_lock = &buffer->locks[loc];
pthread_spin_lock(cell_lock);
buffer->states[loc] = SET;
pthread_spin_unlock(cell_lock);
pthread_spin_lock(&buffer->waiting_readers_lock);
if (buffer->waiting_readers > 0) {
pthread_cond_signal(&buffer->value_written);
}
pthread_spin_unlock(&buffer->waiting_readers_lock);
}
/* just for debugging */
void ring_buffer_dump(const ring_buffer *buffer)
{
printf("RingBuffer\n");
printf(" Capacity: %d\n", buffer->capacity);
printf(" Element size: %d\n", buffer->element_size);
printf(" Read index: %d\n", buffer->read_i);
printf(" Write index: %d\n", buffer->write_i);
printf(" Cells:\n");
for (int i = 0; i < buffer->capacity; ++i) {
printf(" [%d]: STATE = ", i);
switch (buffer->states[i]) {
case NONE:
printf("NONE");
break;
case WRITING:
printf("WRITING");
break;
case READING:
printf("READING");
break;
case SET:
printf("SET");
break;
}
printf("\n");
}
printf("\n");
}
/*
* Test run
*/
struct write_read_n_conf {
ring_buffer *buffer;
int n;
};
static void *producer_thread(void *arg)
{
struct write_read_n_conf conf = *(struct write_read_n_conf *)arg;
for (int i = 0; i < conf.n; ++i) {
ring_buffer_loc loc;
int *value = ring_buffer_write_acquire(conf.buffer, &loc);
*value = i;
ring_buffer_write_finish(conf.buffer, loc);
if (i % 1000 == 0) {
printf("%d / %d\n", i, conf.n);
}
}
return NULL;
}
static void *consumer_thread(void *arg)
{
struct write_read_n_conf conf = *(struct write_read_n_conf *)arg;
int tmp;
bool ok = true;
for (int i = 0; i < conf.n; ++i) {
ring_buffer_loc loc;
const int *value = ring_buffer_read_acquire(conf.buffer, &loc);
tmp = *value;
ring_buffer_read_finish(conf.buffer, loc);
ok = ok && (tmp == i);
}
printf("ok = %d\n", ok);
return (void *)ok;
}
void write_read_n_parallel(int n)
{
ring_buffer *buffer = ring_buffer_create(50, sizeof(int));
struct write_read_n_conf conf = {
.buffer = buffer,
.n = n
};
pthread_t consumer;
pthread_t producer;
pthread_create(&consumer, NULL, consumer_thread, &conf);
pthread_create(&producer, NULL, producer_thread, &conf);
pthread_join(producer, NULL);
void *res;
pthread_join(consumer, &res); // hacky way to pass a bool: res == NULL means false, and otherwise true
assert(res != NULL);
}
int main() {
write_read_n_parallel(10000000);
}

OpenSSL 1.1 result in error: variable ‘mtcp_methods_sockp’ has initializer but incomplete type static BIO_METHOD mtcp_methods_sockp

I am running apache bench (ab) and ported it with OpenSSL 1.0 support on top of mTCP support
https://github.com/vincentmli/mtcp/commit/642835f786aa642ea0f20fe8db9b09054639453a#diff-02f7a72f514e6e0543e832d37450c251
but it breaks after I upgraded OpenSSL 1.0 to OpenSSL 1.1, I found there are quite a few projects having same problems including libevent, libevent has the fix in https://github.com/libevent/libevent/commit/3e9e0a0d46e4508e8782ec3787c6d86bab63046d
so I borrowed the fix from libevent and applied the code fix below:
#include <openssl/bio.h>
#include "openssl-compat.h"
#define get_last_socket_error() errno
#define clear_socket_error() errno=0
/* clone of openssl crypto/bio/bss_sock.c */
#ifdef HAVE_MTCP
static int mtcp_sock_write(BIO *h, const char *buf, int num);
static int mtcp_sock_read(BIO *h, char *buf, int size);
static int mtcp_sock_puts(BIO *h, const char *str);
static long mtcp_sock_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int mtcp_sock_new(BIO *h);
static int mtcp_sock_free(BIO *data);
int BIO_mtcp_sock_should_retry(int s);
/*
static BIO_METHOD mtcp_methods_sockp = {
BIO_TYPE_SOCKET,
"socket",
mtcp_sock_write,
mtcp_sock_read,
mtcp_sock_puts,
NULL,
mtcp_sock_ctrl,
mtcp_sock_new,
mtcp_sock_free,
NULL,
};
BIO_METHOD *BIO_mtcp_s_socket(void)
{
return (&mtcp_methods_sockp);
}
*/
static BIO_METHOD *mtcp_methods_sockp;
static BIO_METHOD *
BIO_mtcp_s_socket(void)
{
if (mtcp_methods_sockp == NULL) {
mtcp_methods_sockp = BIO_meth_new(BIO_TYPE_SOCKET, "socket");
if (mtcp_methods_sockp == NULL)
return NULL;
BIO_meth_set_write(mtcp_methods_sockp, mtcp_sock_write);
BIO_meth_set_read(mtcp_methods_sockp, mtcp_sock_read);
BIO_meth_set_puts(mtcp_methods_sockp, mtcp_sock_puts);
BIO_meth_set_ctrl(mtcp_methods_sockp, mtcp_sock_ctrl);
BIO_meth_set_create(mtcp_methods_sockp, mtcp_sock_new);
BIO_meth_set_destroy(mtcp_methods_sockp, mtcp_sock_free);
}
return mtcp_methods_sockp;
}
/*
BIO *BIO_mtcp_new_socket(mctx_t mctx, int fd, int close_flag)
{
BIO *ret;
ret = BIO_new(BIO_mtcp_s_socket());
if (ret == NULL)
return (NULL);
BIO_set_fd(ret, fd, close_flag);
ret->ptr = mctx;
return (ret);
}
*/
BIO *BIO_mtcp_new_socket(mctx_t mctx, int fd, int close_flag)
{
BIO *ret;
if(!fd)
return NULL;
ret = BIO_new(BIO_mtcp_s_socket());
if (ret == NULL)
return (NULL);
BIO_set_init(ret, 1);
BIO_set_fd(ret, fd, close_flag);
BIO_set_data(ret, mctx);
return (ret);
}
/*
static int mtcp_sock_new(BIO *bi)
{
bi->init = 0;
bi->num = 0;
bi->ptr = NULL;
bi->flags = 0;
return (1);
}
*/
static int mtcp_sock_new(BIO *bi)
{
BIO_set_init(bi, 0);
BIO_set_num(bi, 0);
BIO_set_data(bi, NULL);
BIO_set_flags(bi, 0);
return (1);
}
/*
static int mtcp_sock_free(BIO *a)
{
if (a == NULL)
return (0);
mctx_t mctx = (mctx_t)a->ptr;
mtcp_close(mctx, a->num);
return (1);
}
*/
static int mtcp_sock_free(BIO *a)
{
if (a == NULL)
return (0);
mctx_t mctx = (mctx_t)BIO_get_data(a);
mtcp_close(mctx, BIO_get_num(a));
return (1);
}
/*
static int mtcp_sock_read(BIO *b, char *out, int outl)
{
int ret = 0;
mctx_t mctx = (mctx_t)b->ptr;
if (out != NULL) {
clear_socket_error();
//ret = readsocket(b->num, out, outl);
ret = mtcp_read(mctx, b->num, out, outl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (BIO_mtcp_sock_should_retry(ret))
BIO_set_retry_read(b);
}
}
return (ret);
}
*/
static int mtcp_sock_read(BIO *b, char *out, int outl)
{
int ret = 0;
mctx_t mctx = (mctx_t)BIO_get_data(b);
if (out != NULL) {
clear_socket_error();
//ret = readsocket(b->num, out, outl);
ret = mtcp_read(mctx, BIO_get_num(b), out, outl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (BIO_mtcp_sock_should_retry(ret))
BIO_set_retry_read(b);
}
}
return (ret);
}
/*
static int mtcp_sock_write(BIO *b, const char *in, int inl)
{
int ret;
mctx_t mctx = (mctx_t)b->ptr;
clear_socket_error();
//ret = writesocket(b->num, in, inl);
ret = mtcp_write(mctx, b->num, in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (BIO_mtcp_sock_should_retry(ret))
BIO_set_retry_write(b);
}
return (ret);
}
*/
static int mtcp_sock_write(BIO *b, const char *in, int inl)
{
int ret;
mctx_t mctx = (mctx_t)BIO_get_data(b);
clear_socket_error();
//ret = writesocket(b->num, in, inl);
ret = mtcp_write(mctx, BIO_get_num(b), in, inl);
BIO_clear_retry_flags(b);
if (ret <= 0) {
if (BIO_mtcp_sock_should_retry(ret))
BIO_set_retry_write(b);
}
return (ret);
}
/*
static long mtcp_sock_ctrl(BIO *b, int cmd, long num, void *ptr)
{
long ret = 1;
int *ip;
switch (cmd) {
case BIO_C_SET_FD:
mtcp_sock_free(b);
b->num = *((int *)ptr);
b->shutdown = (int)num;
b->init = 1;
break;
case BIO_C_GET_FD:
if (b->init) {
ip = (int *)ptr;
if (ip != NULL)
*ip = b->num;
ret = b->num;
} else
ret = -1;
break;
case BIO_CTRL_GET_CLOSE:
ret = b->shutdown;
break;
case BIO_CTRL_SET_CLOSE:
b->shutdown = (int)num;
break;
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
ret = 1;
break;
default:
ret = 0;
break;
}
return (ret);
}
*/
static long mtcp_sock_ctrl(BIO *b, int cmd, long num, void *ptr)
{
long ret = 1;
int *ip;
switch (cmd) {
case BIO_C_SET_FD:
mtcp_sock_free(b);
BIO_set_num(b, *((int *)ptr));
BIO_set_shutdown(b, (int)num);
BIO_set_init(b, 1);
break;
case BIO_C_GET_FD:
if (BIO_get_init(b)) {
ip = (int *)ptr;
if (ip != NULL)
*ip = BIO_get_num(b);
ret = BIO_get_num(b);
} else
ret = -1;
break;
case BIO_CTRL_GET_CLOSE:
ret = BIO_get_shutdown(b);
break;
case BIO_CTRL_SET_CLOSE:
BIO_set_shutdown(b, (int)num);
break;
case BIO_CTRL_DUP:
case BIO_CTRL_FLUSH:
ret = 1;
break;
default:
ret = 0;
break;
}
return (ret);
}
here is the openssl-compat.h
#ifndef OPENSSL_COMPAT_H
#define OPENSSL_COMPAT_H
#include <openssl/bio.h>
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
static inline BIO_METHOD *BIO_meth_new(int type, const char *name)
{
BIO_METHOD *biom = calloc(1, sizeof(BIO_METHOD));
if (biom != NULL) {
biom->type = type;
biom->name = name;
}
return biom;
}
#define BIO_meth_set_write(b, f) (b)->bwrite = (f)
#define BIO_meth_set_read(b, f) (b)->bread = (f)
#define BIO_meth_set_puts(b, f) (b)->bputs = (f)
#define BIO_meth_set_ctrl(b, f) (b)->ctrl = (f)
#define BIO_meth_set_create(b, f) (b)->create = (f)
#define BIO_meth_set_destroy(b, f) (b)->destroy = (f)
#define BIO_set_init(b, val) (b)->init = (val)
#define BIO_set_data(b, val) (b)->ptr = (val)
#define BIO_set_shutdown(b, val) (b)->shutdown = (val)
#define BIO_set_num(b, val) (b)->num = (val)
#define BIO_set_flags(b, val) (b)->flags = (val)
#define BIO_get_init(b) (b)->init
#define BIO_get_data(b) (b)->ptr
#define BIO_get_shutdown(b) (b)->shutdown
#define BIO_get_num(b) (b)->num
#define BIO_get_flags(b) (b)->flags
#define TLS_method SSLv23_method
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) */
#endif /* OPENSSL_COMPAT_H */
but I got compiling error
make[2]: Entering directory '/usr/src/mtcp/apps/apache_benchmark/support'
/usr/src/mtcp/apps/apache_benchmark/srclib/apr/libtool --silent --mode=compile gcc -g -O2 -pthread -I/usr/src/mtcp/mtcp/lib/ -I/usr/src/mtcp/mtcp/src/include/ -DMULTI_THREADED -I/usr/src/mtcp/mtcp/lib/ -I/usr/src/mtcp/mtcp/src/include/ -DMULTI_THREADED -DLINUX -D_REENTRANT -D_GNU_SOURCE -I/usr/src/mtcp/apps/apache_benchmark/srclib/pcre -I. -I/usr/src/mtcp/apps/apache_benchmark/os/unix -I/usr/src/mtcp/apps/apache_benchmark/include -I/usr/src/mtcp/apps/apache_benchmark/srclib/apr/include -I/usr/src/mtcp/apps/apache_benchmark/srclib/apr-util/include -I/usr/src/mtcp/apps/apache_benchmark/srclib/apr-util/xml/expat/lib -I/usr/lib/include -I/usr/src/mtcp/apps/apache_benchmark/modules/ssl -prefer-non-pic -static -c ab.c && touch ab.lo
ab.c: In function ‘mtcp_sock_new’:
ab.c:547:5: warning: implicit declaration of function ‘BIO_set_num’; did you mean ‘BIO_set_next’? [-Wimplicit-function-declaration]
BIO_set_num(bi, 0);
^~~~~~~~~~~
BIO_set_next
ab.c: In function ‘mtcp_sock_free’:
ab.c:570:22: warning: implicit declaration of function ‘BIO_get_num’; did you mean ‘BIO_get_init’? [-Wimplicit-function-declaration]
mtcp_close(mctx, BIO_get_num(a));
^~~~~~~~~~~
BIO_get_init
..........CUT......
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:547: undefined reference to `BIO_set_num'
.libs/ab.o: In function `mtcp_sock_free':
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:570: undefined reference to `BIO_get_num'
.libs/ab.o: In function `mtcp_sock_ctrl':
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:703: undefined reference to `BIO_get_num'
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:704: undefined reference to `BIO_get_num'
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:695: undefined reference to `BIO_set_num'
.libs/ab.o: In function `mtcp_sock_read':
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:603: undefined reference to `BIO_get_num'
.libs/ab.o: In function `mtcp_sock_write':
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:638: undefined reference to `BIO_get_num'
.libs/ab.o: In function `main':
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:2916: undefined reference to `SSLv3_client_method'
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:2913: undefined reference to `SSLv2_client_method'
/usr/src/mtcp/apps/apache_benchmark/support/ab.c:2978: undefined reference to `CRYPTO_malloc_init'
collect2: error: ld returned 1 exit status
Makefile:38: recipe for target 'ab' failed
make[2]: *** [ab] Error 1
Since I defined BIO_set/get_init/data/shutdown/num in openssl-compat.h, why it only shows undefined reference to `BIO_set/set_num', not others? I am confused

How to have an array with multiple types of objects, or a function, in C

Wondering if you can do something like this to store arbitrary objects in an array in C:
void *arr[123];
int len = 0;
void
pusharr(void *object) {
arr[len++] = &object;
}
int
main() {
char *foo = "foo"
pusharr(1)
pusharr("foo")
pusharr(&foo)
pusharr(foo)
pusharr(somestruct)
pusharr(someotherstructtype)
pusharr(afunction)
pusharr(anythingbasically)
pusharr(true)
pusharr(NULL)
// arr[4] == somestruct, etc.
}
Basically I'm trying to model like the free(void *ptr) function and pass generic pointers to any possible object type into the function, so it can save references to them. Wondering if that's possible, and if not this way then how.
In terms of functions, it's like this...
So there is this which shows how to pass in a void pointer to get arbitrary types out of a function.
void foo(char* szType, void *pOut) {
switch (szType[0]) {
case 'I': *(int*)pOut = 1; break;
case 'F': *(float*)pOut = 1; break;
}
}
int a;
float b;
foo("I", &a);
foo("F", &b);
I'm wondering if there is a way to do this but attach it to an object/struct.
struct mydataobject {
void *value;
}
This way you could have the function at least return a type.
mydataobject
foo() {
}
In my case I want to have 2 functions, push and pop that work on arbitrary data.
void
mypush(mydataobject something) {
arr[index++] = something
}
mydataobject
mypop() {
return arr[index--]
}
mydataobject a = { "foo" }
mydataobject b = { 123 }
mydataobject c = { true }
mydataobject d = { a }
// it should work with arbitrary data.
Wondering if anything like this is possible.
Yes, this is possible. Just very difficult to do correctly.
Think of scripting languages like Perl, Python or Javascript. Each of those use variables that can hold different types of values. Each of those scripting languages is written in C.
So how do they do it?
Generally they use unions and type tags. A type tag is often an integer like you're using as szType. Sometimes they are a pointer to a structure with data about the type. Sometimes it is a combination, because integer tags are all below 0x1000 (for example) so any number larger must be a pointer.
So design a C union that can hold data about all of your data types. Include a pointer so that extra-large types do not have to make every type huge. Then design a struct that holds a type tag and one of your unions.
Then for every function you create to manipulate these structs, check the type tags and do the correct operations for each one.
I was bored. Here is some code. Note that this is C99 so it won't compile in older versions of Visual Studio (VS 2017 worked!). I used gcc and clang to compile it. Tested with valgrind so no memory leaks. After building it with
gcc -Wall -Wextra -Werror -pedantic -g -O0 type-union-test.c -o type-union-test
run it with
./type-union-test 11 bb 22333333 dd 10 a 11 b
And the code for type-union-test.c: (also available at https://github.com/zlynx/type-union-test )
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// log10(2^64) + 2
#define MAX_INTSTRING_LEN 21
enum VAL_types {
VAL_UNDEFINED,
VAL_INT32,
VAL_STRING,
VAL_OBJECT,
};
enum OPS_RESULT_errors {
OPS_RESULT_OK,
OPS_RESULT_FALSE,
OPS_RESULT_UNIMPLEMENTED,
OPS_RESULT_INVALID_TYPE,
OPS_RESULT_INVALID_INTEGER,
};
struct VAL;
struct OBJECT;
union VAL_type_data {
int32_t int32;
char *string;
struct OBJECT *object;
};
typedef struct OPS_RESULT {
struct VAL *val;
enum OPS_RESULT_errors error;
} OPS_RESULT;
typedef struct VAL_OPS {
OPS_RESULT (*set_type)(struct VAL *, enum VAL_types);
OPS_RESULT (*copy_from_int32)(struct VAL *, int32_t);
OPS_RESULT (*copy_from_string)(struct VAL *, const char *);
OPS_RESULT (*move_from_key_val)(struct VAL *, struct VAL *, struct VAL *);
OPS_RESULT (*is_equal)(struct VAL *, struct VAL *);
OPS_RESULT (*debug_print)(struct VAL *);
} VAL_OPS;
typedef struct VAL {
enum VAL_types type_id;
size_t ref_count;
union VAL_type_data type_data;
const VAL_OPS *ops;
bool constant;
bool owned_ptr;
} VAL;
typedef struct OBJECT_KV {
VAL *key;
VAL *val;
} OBJECT_KV;
typedef struct OBJECT {
OBJECT_KV *array;
size_t len;
size_t cap;
} OBJECT;
OBJECT *OBJECT_new(void);
void OBJECT_delete(OBJECT *op);
VAL *VAL_new(void);
void VAL_delete(VAL *vp);
bool result_ok(OPS_RESULT res) { return res.error == OPS_RESULT_OK; }
const char *result_error_str(enum OPS_RESULT_errors err) {
switch (err) {
case OPS_RESULT_OK:
return "OK";
case OPS_RESULT_FALSE:
return "false";
case OPS_RESULT_UNIMPLEMENTED:
return "unimplemented";
case OPS_RESULT_INVALID_TYPE:
return "invalid type";
case OPS_RESULT_INVALID_INTEGER:
return "invalid integer";
default:
return "unknown error";
}
}
void result_print(OPS_RESULT res) {
FILE *out = stdout;
fprintf(out, "{error: \"%s\"", result_error_str(res.error));
if (result_ok(res) && res.val) {
res.val->ops->debug_print(res.val);
}
fprintf(out, "}");
}
VAL *result_unwrap(OPS_RESULT res) {
if (res.error != OPS_RESULT_OK) {
result_print(res);
printf("\n");
fflush(stdout);
abort();
}
return res.val;
}
void *xmalloc(size_t bytes) {
void *p = malloc(bytes);
if (!p)
abort();
return p;
}
void xfree(void *p) { free(p); }
void xrealloc(void **p, size_t bytes) {
void *new_p = realloc(*p, bytes);
if (!new_p)
abort();
*p = new_p;
}
// Got to take into account the virtual functions we are not using yet!
// One val may have reimplemented is_equal so check both ways. For SCIENCE!
// And unnecessary complexity!
OPS_RESULT VAL_is_equal(VAL *v1_p, VAL *v2_p) {
if (result_ok(v1_p->ops->is_equal(v1_p, v2_p)) &&
result_ok(v2_p->ops->is_equal(v2_p, v1_p)))
return (OPS_RESULT){.error = OPS_RESULT_OK};
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
}
OPS_RESULT VAL_default_set_type(VAL *vp, enum VAL_types type_id) {
if (vp->type_id != VAL_UNDEFINED && vp->type_id != type_id)
// Would need to implement type conversion.
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
vp->type_id = type_id;
switch (type_id) {
case VAL_OBJECT:
vp->type_data.object = OBJECT_new();
break;
default:
// Do nothing special.
break;
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_copy_from_int32(VAL *vp, int32_t source) {
int r;
switch (vp->type_id) {
case VAL_INT32:
vp->type_data.int32 = source;
break;
case VAL_STRING:
if (vp->type_data.string)
xfree(vp->type_data.string);
vp->type_data.string = xmalloc(MAX_INTSTRING_LEN);
r = snprintf(vp->type_data.string, MAX_INTSTRING_LEN, "%d", source);
if (r >= MAX_INTSTRING_LEN)
abort();
break;
default:
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_copy_from_string(VAL *vp, const char *s) {
int r;
char *cp;
long lval;
switch (vp->type_id) {
case VAL_INT32:
errno = 0;
lval = strtol(s, &cp, 0);
if (errno == ERANGE || !(*cp == '\0' || isspace(*cp)) ||
!(lval <= INT_MAX && lval >= INT_MIN))
return (OPS_RESULT){.error = OPS_RESULT_INVALID_INTEGER};
vp->type_data.int32 = lval;
break;
case VAL_STRING:
if (vp->type_data.string)
xfree(vp->type_data.string);
r = strlen(s);
vp->type_data.string = xmalloc(r + 1);
strcpy(vp->type_data.string, s);
break;
default:
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
// This is a move because it does not increment reference counts of key or val.
OPS_RESULT VAL_default_move_from_key_val(VAL *vp, VAL *key, VAL *val) {
// Must be an OBJECT
if (vp->type_id != VAL_OBJECT)
return (OPS_RESULT){.error = OPS_RESULT_INVALID_TYPE};
// Find existing key
size_t i;
for (i = 0; i < vp->type_data.object->len; i++) {
if (result_ok(VAL_is_equal(vp->type_data.object->array[i].key, key))) {
// Delete existing key and value
VAL_delete(vp->type_data.object->array[i].key);
VAL_delete(vp->type_data.object->array[i].val);
break;
}
}
// Insert new key and value
if (i == vp->type_data.object->len) {
// Might have to realloc.
if (i == vp->type_data.object->cap) {
if (vp->type_data.object->cap > 0)
vp->type_data.object->cap *= 2;
else
vp->type_data.object->cap = 4;
xrealloc((void **)&vp->type_data.object->array,
vp->type_data.object->cap * sizeof *vp->type_data.object->array);
}
vp->type_data.object->len++;
}
vp->type_data.object->array[i].key = key;
vp->type_data.object->array[i].val = val;
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_is_equal(VAL *v1_p, VAL *v2_p) {
// Not going to do type conversion right now.
if (v1_p->type_id != v2_p->type_id)
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
switch (v1_p->type_id) {
case VAL_INT32:
if (v1_p->type_data.int32 != v2_p->type_data.int32)
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
break;
case VAL_STRING:
if (strcmp(v1_p->type_data.string, v2_p->type_data.string) != 0)
return (OPS_RESULT){.error = OPS_RESULT_FALSE};
break;
default:
// Not going to compare OBJECTS right now. Too hard.
return (OPS_RESULT){.error = OPS_RESULT_UNIMPLEMENTED};
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
OPS_RESULT VAL_default_debug_print(VAL *vp) {
FILE *out = stdout;
size_t i;
switch (vp->type_id) {
case VAL_INT32:
fprintf(out, "%d", vp->type_data.int32);
break;
case VAL_STRING:
fprintf(out, "\"%s\"", vp->type_data.string);
break;
case VAL_OBJECT:
fprintf(out, "{");
for (i = 0; i < vp->type_data.object->len; i++) {
if (i > 0)
fprintf(out, ", ");
vp->type_data.object->array[i].key->ops->debug_print(
vp->type_data.object->array[i].key);
fprintf(out, ": ");
vp->type_data.object->array[i].val->ops->debug_print(
vp->type_data.object->array[i].val);
}
fprintf(out, "}");
break;
default:
fprintf(out, "\"undefined type\"");
break;
}
return (OPS_RESULT){.error = OPS_RESULT_OK};
}
static const VAL_OPS VAL_OPS_template = {
.set_type = VAL_default_set_type,
.copy_from_int32 = VAL_default_copy_from_int32,
.copy_from_string = VAL_default_copy_from_string,
.move_from_key_val = VAL_default_move_from_key_val,
.is_equal = VAL_default_is_equal,
.debug_print = VAL_default_debug_print,
};
static const VAL VAL_template = {.type_id = VAL_UNDEFINED,
.ref_count = 1,
.type_data = {0},
.ops = &VAL_OPS_template,
.constant = false,
.owned_ptr = false};
VAL *VAL_new(void) {
VAL *p = xmalloc(sizeof *p);
*p = VAL_template;
return p;
}
void VAL_delete(VAL *vp) {
if (--vp->ref_count == 0) {
switch (vp->type_id) {
case VAL_STRING:
xfree(vp->type_data.string);
break;
case VAL_OBJECT:
OBJECT_delete(vp->type_data.object);
break;
default:
// Do nothing.
break;
}
xfree(vp);
}
}
static const OBJECT OBJECT_template = {0};
OBJECT *OBJECT_new(void) {
OBJECT *p = xmalloc(sizeof *p);
*p = OBJECT_template;
return p;
}
void OBJECT_delete(OBJECT *op) {
for (size_t i = 0; i < op->len; i++) {
VAL_delete(op->array[i].key);
VAL_delete(op->array[i].val);
}
xfree(op->array);
xfree(op);
}
int main(int argc, char *argv[]) {
VAL *top = VAL_new();
result_unwrap(top->ops->set_type(top, VAL_OBJECT));
for (int i = 1; i < argc - 1; i += 2) {
VAL *key = VAL_new();
VAL *val = VAL_new();
result_unwrap(key->ops->set_type(key, VAL_INT32));
// key->ops->copy_from_int32(key, i);
result_unwrap(key->ops->copy_from_string(key, argv[i]));
result_unwrap(val->ops->set_type(val, VAL_STRING));
// val->ops->copy_from_string(val, argv[i]);
result_unwrap(val->ops->copy_from_string(val, argv[i + 1]));
result_unwrap(top->ops->move_from_key_val(top, key, val));
}
top->ops->debug_print(top);
printf("\n");
VAL_delete(top);
return 0;
}
Store union Objects
A union lets you hold one of several different types. If the first member is some kind of enum that declares which member of the union is active, that’s called a discriminated union.
Store void Pointers
A void* can reference any type of object, so if the objects themselves exist outside the array, the array can hold pointers to them. You still need some way to remember what the type of the objects was, such as a structure containing both an enum and a void*.
Store Arrays of char
An array of char or unsigned char can hold the object representation of any object its size or smaller. Make sure that the arrays are aligned to max_align_t to guarantee that they have the correct alignment to store any datatype, or just specify multiple types that are required to be correctly aligned. You could cast the char* to the correct type of pointer.
Using a union handles this all for you..

Manipulating structs with a void function in C

so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.
I've already been able to successfully implement length, get, set and copy functions for my faux string structs.
The struct:
struct text {
int capacity;
char *content;
};
typedef struct text text;
My function for creating new structs:
text *newText(char *s) {
printf("new Text from %s\n", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %d\n", sizeGot);
return out;
free(c);
}
My append function:
void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}
and finally the tests:
void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}
You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:
typedef struct {
int capacity;
char content[];
} text;
text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...
And obviously code such as this is nonsense:
return out;
free(c);
}
Enable compiler warnings and listen to them.
Och, some errors you have:
Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.
I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.
Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).
int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.
Try this:
size_t text_newsize(size_t sizeNeeded)
{
// I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot *= 2;
}
return sizeGot;
}
text *newText(char *s) {
printf("new Text from %s\n", s);
if (s == NULL) return NULL;
int sizeNeeded = strlen(s) + 1;
int sizeGot = text_newsize(sizeNeeded);
text *out = malloc(sizeof(*out));
if (out == NULL) {
return NULL;
}
out->content = malloc(sizeGot);
if (out->content == NULL) {
free(out);
return NULL;
}
strcpy(out->content, s);
out->capacity = sizeGot;
printf("the capacity is %d\n", sizeGot);
return out;
}
and this:
int append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
if (t1->capacity < sizeNeeded) {
// this could a text_resize(text*, size_t) function
int sizeGot = text_newsize(sizeNeeded);
void *tmp = realloc(t1->content, sizeGot);
if (tmp == NULL) return -ENOMEM;
t1->content = tmp;
t1->capacity = sizeGot;
}
strcat(t1->content, t2->content);
return 0;
}
Some remarks:
Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.
Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.
I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.
I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.
// text.h file
#ifndef TEXT_H_
#define TEXT_H_
#include <stddef.h>
#include <stdbool.h>
struct text;
typedef struct text text;
text *text_new(const char content[]);
void text_free(text *t);
int text_resize(text *t, size_t newsize);
int text_append(text *to, const text *from);
int text_append_mem(text *to, const void *from, size_t from_len);
const char *text_get(const text *t);
int text_append_str(text *to, const char *from);
char *text_get_nonconst(text *t);
size_t text_getCapacity(const text *t);
bool text_equal(const text *t1, const text *t2);
#endif // TEXT_H_
// text.c file
//#include "text.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
struct text {
size_t capacity;
char *content;
};
text *text_new(const char content[])
{
text * const t = malloc(sizeof(*t));
if (t == NULL) goto MALLOC_ERR;
const struct text zero = {
.capacity = 0,
.content = NULL,
};
*t = zero;
if (content != NULL) {
const int ret = text_append_str(t, content);
if (ret) {
goto TEXT_APPEND_ERR;
}
}
return t;
TEXT_APPEND_ERR:
free(t);
MALLOC_ERR:
return NULL;
}
void text_free(text *t)
{
assert(t != NULL);
free(t->content);
free(t);
}
int text_resize(text *t, size_t newcapacity)
{
// printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
// we resize in chunks
const size_t chunksize = 24;
// clap the capacity into multiple of 24
newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
void * const tmp = realloc(t->content, newcapacity);
if (tmp == NULL) return -ENOMEM;
t->content = tmp;
t->capacity = newcapacity;
return 0;
}
int text_append_mem(text *to, const void *from, size_t from_len)
{
if (to == NULL || from == NULL) return -EINVAL;
if (from_len == 0) return 0;
const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
const size_t newcapacity = oldcapacity + from_len + 1;
int ret = text_resize(to, newcapacity);
if (ret) return ret;
memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
to->content[newcapacity - 1] = '\0';
return 0;
}
int text_append_str(text *to, const char *from)
{
if (to == NULL || from == NULL) return -EINVAL;
return text_append_mem(to, from, strlen(from));
}
int text_append(text *to, const text *from)
{
if (to == NULL || from == NULL) return -EINVAL;
if (text_getCapacity(from) == 0) return 0;
return text_append_str(to, text_get(from));
}
const char *text_get(const text *t)
{
return t->content;
}
const size_t text_strlen(const text *t)
{
return t->capacity == 0 ? 0 : strlen(t->content);
}
size_t text_getCapacity(const text *t)
{
return t->capacity;
}
bool text_equal_str(const text *t, const char *str)
{
assert(t != NULL);
if (str == NULL && t->capacity == 0) return true;
const size_t strlength = strlen(str);
const size_t t_strlen = text_strlen(t);
if (t_strlen != strlength) return false;
if (memcmp(text_get(t), str, strlength) != 0) return false;
return true;
}
// main.c file
#include <stdio.h>
int text_testAppend(void) {
text *t = text_new("car");
if (t == NULL) return -1;
text *t2 = text_new("pet");
if (t2 == NULL) return -1;
if (text_append(t, t2)) return -1;
assert(text_equal_str(t, "carpet"));
assert(text_getCapacity(t) == 24);
text *t3 = text_new("789012345678901234");
if (t3 == NULL) return -1;
if (text_append(t, t3)) return -1;
assert(text_equal_str(t, "carpet789012345678901234"));
assert(text_getCapacity(t) == 48);
text_free(t);
text_free(t2);
text_free(t3);
return 0;
}
int main()
{
text *t1 = text_new("abc");
text_append_str(t1, "def");
printf("%s\n", text_get(t1));
text_free(t1);
printf("text_testAppend = %d\n", text_testAppend());
return 0;
}

Resources