Browse Source
fix build
fix build
24 changed files with 3455 additions and 1 deletions
Split View
Diff Options
-
1.gitignore
-
0libcork/core/.dirstamp
-
131libcork/core/allocator.c
-
246libcork/core/error.c
-
407libcork/core/gc.c
-
20libcork/core/hash.c
-
529libcork/core/ip-address.c
-
179libcork/core/mempool.c
-
162libcork/core/timestamp.c
-
85libcork/core/u128.c
-
75libcork/include/libcork/core/allocator.h
-
25libcork/include/libcork/core/api.h
-
172libcork/include/libcork/core/attributes.h
-
186libcork/include/libcork/core/byte-order.h
-
43libcork/include/libcork/core/callbacks.h
-
139libcork/include/libcork/core/error.h
-
67libcork/include/libcork/core/gc.h
-
356libcork/include/libcork/core/hash.h
-
35libcork/include/libcork/core/id.h
-
59libcork/include/libcork/core/mempool.h
-
147libcork/include/libcork/core/net-addresses.h
-
87libcork/include/libcork/core/timestamp.h
-
82libcork/include/libcork/core/types.h
-
223libcork/include/libcork/core/u128.h
@ -0,0 +1,131 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <stdlib.h> |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
|
|||
#include "libcork/core/allocator.h" |
|||
#include "libcork/core/attributes.h" |
|||
#include "libcork/core/error.h" |
|||
#include "libcork/core/types.h" |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* reallocf |
|||
*/ |
|||
|
|||
#if !CORK_HAVE_REALLOCF |
|||
void * |
|||
cork_xrealloc(void *ptr, size_t new_size) |
|||
{ |
|||
void *result = realloc(ptr, new_size); |
|||
if (result == NULL) { |
|||
free(ptr); |
|||
} |
|||
return result; |
|||
} |
|||
#endif |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Allocating strings |
|||
*/ |
|||
|
|||
static inline const char * |
|||
strndup_internal(const char *str, size_t len) |
|||
{ |
|||
size_t allocated_size = len + sizeof(size_t) + 1; |
|||
size_t *new_str = malloc(allocated_size); |
|||
if (new_str == NULL) { |
|||
return NULL; |
|||
} |
|||
|
|||
*new_str = allocated_size; |
|||
char *dest = (char *) (void *) (new_str + 1); |
|||
strncpy(dest, str, len); |
|||
dest[len] = '\0'; |
|||
return dest; |
|||
} |
|||
|
|||
const char * |
|||
cork_xstrndup(const char *str, size_t len) |
|||
{ |
|||
return strndup_internal(str, len); |
|||
} |
|||
|
|||
const char * |
|||
cork_xstrdup(const char *str) |
|||
{ |
|||
return strndup_internal(str, strlen(str)); |
|||
} |
|||
|
|||
|
|||
void |
|||
cork_strfree(const char *str) |
|||
{ |
|||
size_t *base = ((size_t *) str) - 1; |
|||
free(base); |
|||
} |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Abort on failure |
|||
*/ |
|||
|
|||
void * |
|||
cork_malloc(size_t size) |
|||
{ |
|||
void *result = cork_xmalloc(size); |
|||
if (CORK_UNLIKELY(result == NULL)) { |
|||
abort(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
void * |
|||
cork_calloc(size_t count, size_t size) |
|||
{ |
|||
void *result = cork_xcalloc(count, size); |
|||
if (CORK_UNLIKELY(result == NULL)) { |
|||
abort(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
void * |
|||
cork_realloc(void *ptr, size_t new_size) |
|||
{ |
|||
void *result = cork_xrealloc(ptr, new_size); |
|||
if (CORK_UNLIKELY(result == NULL)) { |
|||
abort(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
const char * |
|||
cork_strdup(const char *src) |
|||
{ |
|||
const char *result = cork_xstrdup(src); |
|||
if (CORK_UNLIKELY(result == NULL)) { |
|||
abort(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
const char * |
|||
cork_strndup(const char *src, size_t size) |
|||
{ |
|||
const char *result = cork_xstrndup(src, size); |
|||
if (CORK_UNLIKELY(result == NULL)) { |
|||
abort(); |
|||
} |
|||
return result; |
|||
} |
@ -0,0 +1,246 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
|
|||
#include "libcork/config.h" |
|||
#include "libcork/core/allocator.h" |
|||
#include "libcork/core/error.h" |
|||
#include "libcork/ds/buffer.h" |
|||
#include "libcork/os/process.h" |
|||
#include "libcork/threads/basics.h" |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Life cycle |
|||
*/ |
|||
|
|||
struct cork_error { |
|||
cork_error code; |
|||
struct cork_buffer *message; |
|||
struct cork_buffer *other; |
|||
struct cork_buffer buf1; |
|||
struct cork_buffer buf2; |
|||
struct cork_error *next; |
|||
}; |
|||
|
|||
static struct cork_error * |
|||
cork_error_new(void) |
|||
{ |
|||
struct cork_error *error = cork_new(struct cork_error); |
|||
error->code = CORK_ERROR_NONE; |
|||
cork_buffer_init(&error->buf1); |
|||
cork_buffer_init(&error->buf2); |
|||
error->message = &error->buf1; |
|||
error->other = &error->buf2; |
|||
return error; |
|||
} |
|||
|
|||
static void |
|||
cork_error_free(struct cork_error *error) |
|||
{ |
|||
cork_buffer_done(&error->buf1); |
|||
cork_buffer_done(&error->buf2); |
|||
free(error); |
|||
} |
|||
|
|||
|
|||
static struct cork_error * volatile errors; |
|||
|
|||
cork_once_barrier(cork_error_list); |
|||
|
|||
static void |
|||
cork_error_list_done(void) |
|||
{ |
|||
struct cork_error *curr; |
|||
struct cork_error *next; |
|||
for (curr = errors; curr != NULL; curr = next) { |
|||
next = curr->next; |
|||
cork_error_free(curr); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_error_list_init(void) |
|||
{ |
|||
cork_cleanup_at_exit(0, cork_error_list_done); |
|||
} |
|||
|
|||
|
|||
cork_tls(struct cork_error *, cork_error_); |
|||
|
|||
static struct cork_error * |
|||
cork_error_get(void) |
|||
{ |
|||
struct cork_error **error_ptr = cork_error__get(); |
|||
if (CORK_UNLIKELY(*error_ptr == NULL)) { |
|||
struct cork_error *old_head; |
|||
struct cork_error *error = cork_error_new(); |
|||
cork_once(cork_error_list, cork_error_list_init()); |
|||
do { |
|||
old_head = errors; |
|||
error->next = old_head; |
|||
} while (cork_ptr_cas(&errors, old_head, error) != old_head); |
|||
*error_ptr = error; |
|||
return error; |
|||
} else { |
|||
return *error_ptr; |
|||
} |
|||
} |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Public error API |
|||
*/ |
|||
|
|||
bool |
|||
cork_error_occurred(void) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
return error->code != CORK_ERROR_NONE; |
|||
} |
|||
|
|||
cork_error |
|||
cork_error_code(void) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
return error->code; |
|||
} |
|||
|
|||
const char * |
|||
cork_error_message(void) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
return error->message->buf; |
|||
} |
|||
|
|||
void |
|||
cork_error_clear(void) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
error->code = CORK_ERROR_NONE; |
|||
cork_buffer_clear(error->message); |
|||
} |
|||
|
|||
void |
|||
cork_error_set_printf(cork_error code, const char *format, ...) |
|||
{ |
|||
va_list args; |
|||
struct cork_error *error = cork_error_get(); |
|||
error->code = code; |
|||
va_start(args, format); |
|||
cork_buffer_vprintf(error->message, format, args); |
|||
va_end(args); |
|||
} |
|||
|
|||
void |
|||
cork_error_set_string(cork_error code, const char *str) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
error->code = code; |
|||
cork_buffer_set_string(error->message, str); |
|||
} |
|||
|
|||
void |
|||
cork_error_set_vprintf(cork_error code, const char *format, va_list args) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
error->code = code; |
|||
cork_buffer_vprintf(error->message, format, args); |
|||
} |
|||
|
|||
void |
|||
cork_error_prefix_printf(const char *format, ...) |
|||
{ |
|||
va_list args; |
|||
struct cork_error *error = cork_error_get(); |
|||
struct cork_buffer *temp; |
|||
va_start(args, format); |
|||
cork_buffer_vprintf(error->other, format, args); |
|||
va_end(args); |
|||
cork_buffer_append_copy(error->other, error->message); |
|||
temp = error->other; |
|||
error->other = error->message; |
|||
error->message = temp; |
|||
} |
|||
|
|||
void |
|||
cork_error_prefix_string(const char *str) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
struct cork_buffer *temp; |
|||
cork_buffer_set_string(error->other, str); |
|||
cork_buffer_append_copy(error->other, error->message); |
|||
temp = error->other; |
|||
error->other = error->message; |
|||
error->message = temp; |
|||
} |
|||
|
|||
void |
|||
cork_error_prefix_vprintf(const char *format, va_list args) |
|||
{ |
|||
struct cork_error *error = cork_error_get(); |
|||
struct cork_buffer *temp; |
|||
cork_buffer_vprintf(error->other, format, args); |
|||
cork_buffer_append_copy(error->other, error->message); |
|||
temp = error->other; |
|||
error->other = error->message; |
|||
error->message = temp; |
|||
} |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Deprecated |
|||
*/ |
|||
|
|||
void |
|||
cork_error_set(uint32_t error_class, unsigned int error_code, |
|||
const char *format, ...) |
|||
{ |
|||
/* Create a fallback error code that's most likely not very useful. */ |
|||
va_list args; |
|||
va_start(args, format); |
|||
cork_error_set_vprintf(error_class + error_code, format, args); |
|||
va_end(args); |
|||
} |
|||
|
|||
void |
|||
cork_error_prefix(const char *format, ...) |
|||
{ |
|||
va_list args; |
|||
va_start(args, format); |
|||
cork_error_prefix_vprintf(format, args); |
|||
va_end(args); |
|||
} |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Built-in errors |
|||
*/ |
|||
|
|||
void |
|||
cork_system_error_set_explicit(int err) |
|||
{ |
|||
cork_error_set_string(err, strerror(err)); |
|||
} |
|||
|
|||
void |
|||
cork_system_error_set(void) |
|||
{ |
|||
cork_error_set_string(errno, strerror(errno)); |
|||
} |
|||
|
|||
void |
|||
cork_unknown_error_set_(const char *location) |
|||
{ |
|||
cork_error_set_printf(CORK_UNKNOWN_ERROR, "Unknown error in %s", location); |
|||
} |
@ -0,0 +1,407 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include "libcork/config/config.h" |
|||
#include "libcork/core/allocator.h" |
|||
#include "libcork/core/gc.h" |
|||
#include "libcork/core/types.h" |
|||
#include "libcork/ds/dllist.h" |
|||
#include "libcork/threads/basics.h" |
|||
|
|||
|
|||
#if !defined(CORK_DEBUG_GC) |
|||
#define CORK_DEBUG_GC 0 |
|||
#endif |
|||
|
|||
#if CORK_DEBUG_GC |
|||
#include <stdio.h> |
|||
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) |
|||
#else |
|||
#define DEBUG(...) /* no debug messages */ |
|||
#endif |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* GC context life cycle |
|||
*/ |
|||
|
|||
#define ROOTS_SIZE 1024 |
|||
|
|||
/* An internal structure allocated with every garbage-collected object. */ |
|||
struct cork_gc_header; |
|||
|
|||
/* A garbage collector context. */ |
|||
struct cork_gc { |
|||
/* The number of used entries in roots. */ |
|||
size_t root_count; |
|||
/* The possible roots of garbage cycles */ |
|||
struct cork_gc_header *roots[ROOTS_SIZE]; |
|||
}; |
|||
|
|||
cork_tls(struct cork_gc, cork_gc); |
|||
|
|||
static void |
|||
cork_gc_collect_cycles(struct cork_gc *gc); |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Garbage collection functions |
|||
*/ |
|||
|
|||
struct cork_gc_header { |
|||
/* The current reference count for this object, along with its color |
|||
* during the mark/sweep process. */ |
|||
volatile int ref_count_color; |
|||
|
|||
/* The allocated size of this garbage-collected object (including |
|||
* the header). */ |
|||
size_t allocated_size; |
|||
|
|||
/* The garbage collection interface for this object. */ |
|||
struct cork_gc_obj_iface *iface; |
|||
}; |
|||
|
|||
/* |
|||
* Structure of ref_count_color: |
|||
* |
|||
* +-----+---+---+---+---+---+ |
|||
* | ... | 4 | 3 | 2 | 1 | 0 | |
|||
* +-----+---+---+---+---+---+ |
|||
* ref_count | color |
|||
* | |
|||
* buffered --/ |
|||
*/ |
|||
|
|||
#define cork_gc_ref_count_color(count, buffered, color) \ |
|||
(((count) << 3) | ((buffered) << 2) | (color)) |
|||
|
|||
#define cork_gc_get_ref_count(hdr) \ |
|||
((hdr)->ref_count_color >> 3) |
|||
|
|||
#define cork_gc_inc_ref_count(hdr) \ |
|||
do { \ |
|||
(hdr)->ref_count_color += (1 << 3); \ |
|||
} while (0) |
|||
|
|||
#define cork_gc_dec_ref_count(hdr) \ |
|||
do { \ |
|||
(hdr)->ref_count_color -= (1 << 3); \ |
|||
} while (0) |
|||
|
|||
#define cork_gc_get_color(hdr) \ |
|||
((hdr)->ref_count_color & 0x3) |
|||
|
|||
#define cork_gc_set_color(hdr, color) \ |
|||
do { \ |
|||
(hdr)->ref_count_color = \ |
|||
((hdr)->ref_count_color & ~0x3) | (color & 0x3); \ |
|||
} while (0) |
|||
|
|||
#define cork_gc_get_buffered(hdr) \ |
|||
(((hdr)->ref_count_color & 0x4) != 0) |
|||
|
|||
#define cork_gc_set_buffered(hdr, buffered) \ |
|||
do { \ |
|||
(hdr)->ref_count_color = \ |
|||
((hdr)->ref_count_color & ~0x4) | (((buffered) & 1) << 2); \ |
|||
} while (0) |
|||
|
|||
#define cork_gc_free(hdr) \ |
|||
do { \ |
|||
if ((hdr)->iface->free != NULL) { \ |
|||
(hdr)->iface->free(cork_gc_get_object((hdr))); \ |
|||
} \ |
|||
free((hdr)); \ |
|||
} while (0) |
|||
|
|||
#define cork_gc_recurse(gc, hdr, recurser) \ |
|||
do { \ |
|||
if ((hdr)->iface->recurse != NULL) { \ |
|||
(hdr)->iface->recurse \ |
|||
((gc), cork_gc_get_object((hdr)), (recurser), NULL); \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
enum cork_gc_color { |
|||
/* In use or free */ |
|||
GC_BLACK = 0, |
|||
/* Possible member of garbage cycle */ |
|||
GC_GRAY = 1, |
|||
/* Member of garbage cycle */ |
|||
GC_WHITE = 2, |
|||
/* Possible root of garbage cycle */ |
|||
GC_PURPLE = 3 |
|||
}; |
|||
|
|||
#define cork_gc_get_header(obj) \ |
|||
(((struct cork_gc_header *) (obj)) - 1) |
|||
|
|||
#define cork_gc_get_object(hdr) \ |
|||
((void *) (((struct cork_gc_header *) (hdr)) + 1)) |
|||
|
|||
|
|||
void |
|||
cork_gc_init(void) |
|||
{ |
|||
cork_gc_get(); |
|||
} |
|||
|
|||
void |
|||
cork_gc_done(void) |
|||
{ |
|||
cork_gc_collect_cycles(cork_gc_get()); |
|||
} |
|||
|
|||
void * |
|||
cork_gc_alloc(size_t instance_size, struct cork_gc_obj_iface *iface) |
|||
{ |
|||
size_t full_size = instance_size + sizeof(struct cork_gc_header); |
|||
DEBUG("Allocating %zu (%zu) bytes\n", instance_size, full_size); |
|||
struct cork_gc_header *header = cork_malloc(full_size); |
|||
DEBUG(" Result is %p[%p]\n", cork_gc_get_object(header), header); |
|||
header->ref_count_color = cork_gc_ref_count_color(1, false, GC_BLACK); |
|||
header->allocated_size = full_size; |
|||
header->iface = iface; |
|||
return cork_gc_get_object(header); |
|||
} |
|||
|
|||
void * |
|||
cork_gc_incref(void *obj) |
|||
{ |
|||
if (obj != NULL) { |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
cork_gc_inc_ref_count(header); |
|||
DEBUG("Incrementing %p -> %d\n", |
|||
obj, cork_gc_get_ref_count(header)); |
|||
cork_gc_set_color(header, GC_BLACK); |
|||
} |
|||
return obj; |
|||
} |
|||
|
|||
static void |
|||
cork_gc_decref_step(struct cork_gc *gc, void *obj, void *ud); |
|||
|
|||
static void |
|||
cork_gc_release(struct cork_gc *gc, struct cork_gc_header *header) |
|||
{ |
|||
cork_gc_recurse(gc, header, cork_gc_decref_step); |
|||
cork_gc_set_color(header, GC_BLACK); |
|||
if (!cork_gc_get_buffered(header)) { |
|||
cork_gc_free(header); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_possible_root(struct cork_gc *gc, struct cork_gc_header *header) |
|||
{ |
|||
if (cork_gc_get_color(header) != GC_PURPLE) { |
|||
DEBUG(" Possible garbage cycle root\n"); |
|||
cork_gc_set_color(header, GC_PURPLE); |
|||
if (!cork_gc_get_buffered(header)) { |
|||
cork_gc_set_buffered(header, true); |
|||
if (gc->root_count >= ROOTS_SIZE) { |
|||
cork_gc_collect_cycles(gc); |
|||
} |
|||
gc->roots[gc->root_count++] = header; |
|||
} |
|||
} else { |
|||
DEBUG(" Already marked as possible garbage cycle root\n"); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_decref_step(struct cork_gc *gc, void *obj, void *ud) |
|||
{ |
|||
if (obj != NULL) { |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
cork_gc_dec_ref_count(header); |
|||
DEBUG("Decrementing %p -> %d\n", |
|||
obj, cork_gc_get_ref_count(header)); |
|||
if (cork_gc_get_ref_count(header) == 0) { |
|||
DEBUG(" Releasing %p\n", header); |
|||
cork_gc_release(gc, header); |
|||
} else { |
|||
cork_gc_possible_root(gc, header); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void |
|||
cork_gc_decref(void *obj) |
|||
{ |
|||
if (obj != NULL) { |
|||
struct cork_gc *gc = cork_gc_get(); |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
cork_gc_dec_ref_count(header); |
|||
DEBUG("Decrementing %p -> %d\n", |
|||
obj, cork_gc_get_ref_count(header)); |
|||
if (cork_gc_get_ref_count(header) == 0) { |
|||
DEBUG(" Releasing %p\n", header); |
|||
cork_gc_release(gc, header); |
|||
} else { |
|||
cork_gc_possible_root(gc, header); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
static void |
|||
cork_gc_mark_gray_step(struct cork_gc *gc, void *obj, void *ud); |
|||
|
|||
static void |
|||
cork_gc_mark_gray(struct cork_gc *gc, struct cork_gc_header *header) |
|||
{ |
|||
if (cork_gc_get_color(header) != GC_GRAY) { |
|||
DEBUG(" Setting color to gray\n"); |
|||
cork_gc_set_color(header, GC_GRAY); |
|||
cork_gc_recurse(gc, header, cork_gc_mark_gray_step); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_mark_gray_step(struct cork_gc *gc, void *obj, void *ud) |
|||
{ |
|||
if (obj != NULL) { |
|||
DEBUG(" cork_gc_mark_gray(%p)\n", obj); |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
cork_gc_dec_ref_count(header); |
|||
DEBUG(" Reference count now %d\n", cork_gc_get_ref_count(header)); |
|||
cork_gc_mark_gray(gc, header); |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_mark_roots(struct cork_gc *gc) |
|||
{ |
|||
size_t i; |
|||
for (i = 0; i < gc->root_count; i++) { |
|||
struct cork_gc_header *header = gc->roots[i]; |
|||
if (cork_gc_get_color(header) == GC_PURPLE) { |
|||
DEBUG(" Checking possible garbage cycle root %p\n", |
|||
cork_gc_get_object(header)); |
|||
DEBUG(" cork_gc_mark_gray(%p)\n", |
|||
cork_gc_get_object(header)); |
|||
cork_gc_mark_gray(gc, header); |
|||
} else { |
|||
DEBUG(" Possible garbage cycle root %p already checked\n", |
|||
cork_gc_get_object(header)); |
|||
cork_gc_set_buffered(header, false); |
|||
gc->roots[i] = NULL; |
|||
if (cork_gc_get_color(header) == GC_BLACK && |
|||
cork_gc_get_ref_count(header) == 0) { |
|||
DEBUG(" Freeing %p\n", header); |
|||
cork_gc_free(header); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_scan_black_step(struct cork_gc *gc, void *obj, void *ud); |
|||
|
|||
static void |
|||
cork_gc_scan_black(struct cork_gc *gc, struct cork_gc_header *header) |
|||
{ |
|||
DEBUG(" Setting color of %p to BLACK\n", |
|||
cork_gc_get_object(header)); |
|||
cork_gc_set_color(header, GC_BLACK); |
|||
cork_gc_recurse(gc, header, cork_gc_scan_black_step); |
|||
} |
|||
|
|||
static void |
|||
cork_gc_scan_black_step(struct cork_gc *gc, void *obj, void *ud) |
|||
{ |
|||
if (obj != NULL) { |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
cork_gc_inc_ref_count(header); |
|||
DEBUG(" Increasing reference count %p -> %d\n", |
|||
obj, cork_gc_get_ref_count(header)); |
|||
if (cork_gc_get_color(header) != GC_BLACK) { |
|||
cork_gc_scan_black(gc, header); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_scan(struct cork_gc *gc, void *obj, void *ud) |
|||
{ |
|||
if (obj != NULL) { |
|||
DEBUG(" Scanning possible garbage cycle entry %p\n", obj); |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
if (cork_gc_get_color(header) == GC_GRAY) { |
|||
if (cork_gc_get_ref_count(header) > 0) { |
|||
DEBUG(" Remaining references; can't be a cycle\n"); |
|||
cork_gc_scan_black(gc, header); |
|||
} else { |
|||
DEBUG(" Definitely a garbage cycle\n"); |
|||
cork_gc_set_color(header, GC_WHITE); |
|||
cork_gc_recurse(gc, header, cork_gc_scan); |
|||
} |
|||
} else { |
|||
DEBUG(" Already checked\n"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_scan_roots(struct cork_gc *gc) |
|||
{ |
|||
size_t i; |
|||
for (i = 0; i < gc->root_count; i++) { |
|||
if (gc->roots[i] != NULL) { |
|||
void *obj = cork_gc_get_object(gc->roots[i]); |
|||
cork_gc_scan(gc, obj, NULL); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_collect_white(struct cork_gc *gc, void *obj, void *ud) |
|||
{ |
|||
if (obj != NULL) { |
|||
struct cork_gc_header *header = cork_gc_get_header(obj); |
|||
if (cork_gc_get_color(header) == GC_WHITE && |
|||
!cork_gc_get_buffered(header)) { |
|||
DEBUG(" Releasing %p\n", obj); |
|||
cork_gc_set_color(header, GC_BLACK); |
|||
cork_gc_recurse(gc, header, cork_gc_collect_white); |
|||
DEBUG(" Freeing %p\n", header); |
|||
cork_gc_free(header); |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void |
|||
cork_gc_collect_roots(struct cork_gc *gc) |
|||
{ |
|||
size_t i; |
|||
for (i = 0; i < gc->root_count; i++) { |
|||
if (gc->roots[i] != NULL) { |
|||
struct cork_gc_header *header = gc->roots[i]; |
|||
void *obj = cork_gc_get_object(header); |
|||
cork_gc_set_buffered(header, false); |
|||
DEBUG("Collecting cycles from garbage root %p\n", obj); |
|||
cork_gc_collect_white(gc, obj, NULL); |
|||
gc->roots[i] = NULL; |
|||
} |
|||
} |
|||
gc->root_count = 0; |
|||
} |
|||
|
|||
static void |
|||
cork_gc_collect_cycles(struct cork_gc *gc) |
|||
{ |
|||
DEBUG("Collecting garbage cycles\n"); |
|||
cork_gc_mark_roots(gc); |
|||
cork_gc_scan_roots(gc); |
|||
cork_gc_collect_roots(gc); |
|||
} |
@ -0,0 +1,20 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#define CORK_HASH_ATTRIBUTES CORK_API |
|||
|
|||
#include "libcork/core/hash.h" |
|||
#include "libcork/core/types.h" |
|||
|
|||
/* All of the following functions will be defined for us by libcork/core/hash.h: |
|||
* cork_hash_buffer |
|||
* cork_big_hash_buffer |
|||
* cork_stable_hash_buffer |
|||
*/ |
@ -0,0 +1,529 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
|
|||
#include "libcork/core/byte-order.h" |
|||
#include "libcork/core/error.h" |
|||
#include "libcork/core/net-addresses.h" |
|||
#include "libcork/core/types.h" |
|||
|
|||
#ifndef CORK_IP_ADDRESS_DEBUG |
|||
#define CORK_IP_ADDRESS_DEBUG 0 |
|||
#endif |
|||
|
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
#include <stdio.h> |
|||
#define DEBUG(...) \ |
|||
do { \ |
|||
fprintf(stderr, __VA_ARGS__); \ |
|||
} while (0) |
|||
#else |
|||
#define DEBUG(...) /* nothing */ |
|||
#endif |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* IP addresses |
|||
*/ |
|||
|
|||
/*** IPv4 ***/ |
|||
|
|||
static inline const char * |
|||
cork_ipv4_parse(struct cork_ipv4 *addr, const char *str) |
|||
{ |
|||
const char *ch; |
|||
bool seen_digit_in_octet = false; |
|||
unsigned int octets = 0; |
|||
unsigned int digit = 0; |
|||
uint8_t result[4]; |
|||
|
|||
for (ch = str; *ch != '\0'; ch++) { |
|||
DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch); |
|||
switch (*ch) { |
|||
case '0': case '1': case '2': case '3': case '4': |
|||
case '5': case '6': case '7': case '8': case '9': |
|||
seen_digit_in_octet = true; |
|||
digit *= 10; |
|||
digit += (*ch - '0'); |
|||
DEBUG("digit = %u\n", digit); |
|||
if (CORK_UNLIKELY(digit > 255)) { |
|||
DEBUG("\t"); |
|||
goto parse_error; |
|||
} |
|||
break; |
|||
|
|||
case '.': |
|||
/* If this would be the fourth octet, it can't have a trailing |
|||
* period. */ |
|||
if (CORK_UNLIKELY(octets == 3)) { |
|||
goto parse_error; |
|||
} |
|||
DEBUG("octet %u = %u\n", octets, digit); |
|||
result[octets] = digit; |
|||
digit = 0; |
|||
octets++; |
|||
seen_digit_in_octet = false; |
|||
break; |
|||
|
|||
default: |
|||
/* Any other character is a parse error. */ |
|||
goto parse_error; |
|||
} |
|||
} |
|||
|
|||
/* If we have a valid octet at the end, and that would be the fourth octet, |
|||
* then we've got a valid final parse. */ |
|||
DEBUG("%2u:\t", (unsigned int) (ch-str)); |
|||
if (CORK_LIKELY(seen_digit_in_octet && octets == 3)) { |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
char parsed_ipv4[CORK_IPV4_STRING_LENGTH]; |
|||
#endif |
|||
DEBUG("octet %u = %u\n", octets, digit); |
|||
result[octets] = digit; |
|||
cork_ipv4_copy(addr, result); |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
cork_ipv4_to_raw_string(addr, parsed_ipv4); |
|||
DEBUG("\tParsed address: %s\n", parsed_ipv4); |
|||
#endif |
|||
return ch; |
|||
} |
|||
|
|||
parse_error: |
|||
DEBUG("parse error\n"); |
|||
cork_parse_error("Invalid IPv4 address: \"%s\"", str); |
|||
return NULL; |
|||
} |
|||
|
|||
int |
|||
cork_ipv4_init(struct cork_ipv4 *addr, const char *str) |
|||
{ |
|||
return cork_ipv4_parse(addr, str) == NULL? -1: 0; |
|||
} |
|||
|
|||
bool |
|||
cork_ipv4_equal_(const struct cork_ipv4 *addr1, const struct cork_ipv4 *addr2) |
|||
{ |
|||
return cork_ipv4_equal(addr1, addr2); |
|||
} |
|||
|
|||
void |
|||
cork_ipv4_to_raw_string(const struct cork_ipv4 *addr, char *dest) |
|||
{ |
|||
snprintf(dest, CORK_IPV4_STRING_LENGTH, "%u.%u.%u.%u", |
|||
addr->_.u8[0], addr->_.u8[1], addr->_.u8[2], addr->_.u8[3]); |
|||
} |
|||
|
|||
bool |
|||
cork_ipv4_is_valid_network(const struct cork_ipv4 *addr, |
|||
unsigned int cidr_prefix) |
|||
{ |
|||
uint32_t cidr_mask; |
|||
|
|||
if (cidr_prefix > 32) { |
|||
return false; |
|||
} else if (cidr_prefix == 32) { |
|||
/* This handles undefined behavior for overflow bit shifts. */ |
|||
cidr_mask = 0; |
|||
} else { |
|||
cidr_mask = 0xffffffff >> cidr_prefix; |
|||
} |
|||
|
|||
return (CORK_UINT32_BIG_TO_HOST(addr->_.u32) & cidr_mask) == 0; |
|||
} |
|||
|
|||
/*** IPv6 ***/ |
|||
|
|||
int |
|||
cork_ipv6_init(struct cork_ipv6 *addr, const char *str) |
|||
{ |
|||
const char *ch; |
|||
|
|||
uint16_t digit = 0; |
|||
unsigned int before_count = 0; |
|||
uint16_t before_double_colon[8]; |
|||
uint16_t after_double_colon[8]; |
|||
uint16_t *dest = before_double_colon; |
|||
|
|||
unsigned int digits_seen = 0; |
|||
unsigned int hextets_seen = 0; |
|||
bool another_required = true; |
|||
bool digit_allowed = true; |
|||
bool colon_allowed = true; |
|||
bool double_colon_allowed = true; |
|||
bool just_saw_colon = false; |
|||
|
|||
for (ch = str; *ch != '\0'; ch++) { |
|||
DEBUG("%2u: %c\t", (unsigned int) (ch-str), *ch); |
|||
switch (*ch) { |
|||
#define process_digit(base) \ |
|||
/* Make sure a digit is allowed here. */ \ |
|||
if (CORK_UNLIKELY(!digit_allowed)) { \ |
|||
goto parse_error; \ |
|||
} \ |
|||
/* If we've already seen 4 digits, it's a parse error. */ \ |
|||
if (CORK_UNLIKELY(digits_seen == 4)) { \ |
|||
goto parse_error; \ |
|||
} \ |
|||
\ |
|||
digits_seen++; \ |
|||
colon_allowed = true; \ |
|||
just_saw_colon = false; \ |
|||
digit <<= 4; \ |
|||
digit |= (*ch - (base)); \ |
|||
DEBUG("digit = %04x\n", digit); |
|||
|
|||
case '0': case '1': case '2': case '3': case '4': |
|||
case '5': case '6': case '7': case '8': case '9': |
|||
process_digit('0'); |
|||
break; |
|||
|
|||
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': |
|||
process_digit('a'-10); |
|||
break; |
|||
|
|||
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': |
|||
process_digit('A'-10); |
|||
break; |
|||
|
|||
#undef process_digit |
|||
|
|||
case ':': |
|||
/* We can only see a colon immediately after a hextet or as part |
|||
* of a double-colon. */ |
|||
if (CORK_UNLIKELY(!colon_allowed)) { |
|||
goto parse_error; |
|||
} |
|||
|
|||
/* If this is a double-colon, start parsing hextets into our |
|||
* second array. */ |
|||
if (just_saw_colon) { |
|||
DEBUG("double-colon\n"); |
|||
colon_allowed = false; |
|||
digit_allowed = true; |
|||
another_required = false; |
|||
double_colon_allowed = false; |
|||
before_count = hextets_seen; |
|||
dest = after_double_colon; |
|||
continue; |
|||
} |
|||
|
|||
/* If this would end the eighth hextet (regardless of the |
|||
* placement of a double-colon), then there can't be a trailing |
|||
* colon. */ |
|||
if (CORK_UNLIKELY(hextets_seen == 8)) { |
|||
goto parse_error; |
|||
} |
|||
|
|||
/* If this is the very beginning of the string, then we can only |
|||
* have a double-colon, not a single colon. */ |
|||
if (digits_seen == 0 && hextets_seen == 0) { |
|||
DEBUG("initial colon\n"); |
|||
colon_allowed = true; |
|||
digit_allowed = false; |
|||
just_saw_colon = true; |
|||
another_required = true; |
|||
continue; |
|||
} |
|||
|
|||
/* Otherwise this ends the current hextet. */ |
|||
DEBUG("hextet %u = %04x\n", hextets_seen, digit); |
|||
*(dest++) = CORK_UINT16_HOST_TO_BIG(digit); |
|||
digit = 0; |
|||
hextets_seen++; |
|||
digits_seen = 0; |
|||
colon_allowed = double_colon_allowed; |
|||
just_saw_colon = true; |
|||
another_required = true; |
|||
break; |
|||
|
|||
case '.': |
|||
{ |
|||
/* If we see a period, then we must be in the middle of an IPv4 |
|||
* address at the end of the IPv6 address. */ |
|||
struct cork_ipv4 *ipv4 = (struct cork_ipv4 *) dest; |
|||
DEBUG("Detected IPv4 address %s\n", ch-digits_seen); |
|||
|
|||
/* Ensure that we have space for the two hextets that the IPv4 |
|||
* address will take up. */ |
|||
if (CORK_UNLIKELY(hextets_seen >= 7)) { |
|||
goto parse_error; |
|||
} |
|||
|
|||
/* Parse the IPv4 address directly into our current hextet |
|||
* buffer. */ |
|||
ch = cork_ipv4_parse(ipv4, ch - digits_seen); |
|||
if (CORK_LIKELY(ch != NULL)) { |
|||
hextets_seen += 2; |
|||
digits_seen = 0; |
|||
another_required = false; |
|||
|
|||
/* ch now points at the NUL terminator, but we're about to |
|||
* increment ch. */ |
|||
ch--; |
|||
break; |
|||
} |
|||
|
|||
/* The IPv4 parse failed, so we have an IPv6 parse error. */ |
|||
goto parse_error; |
|||
} |
|||
|
|||
default: |
|||
/* Any other character is a parse error. */ |
|||
goto parse_error; |
|||
} |
|||
} |
|||
|
|||
/* If we have a valid hextet at the end, and we've either seen a |
|||
* double-colon, or we have eight hextets in total, then we've got a valid |
|||
* final parse. */ |
|||
DEBUG("%2u:\t", (unsigned int) (ch-str)); |
|||
if (CORK_LIKELY(digits_seen > 0)) { |
|||
DEBUG("hextet %u = %04x\n\t", hextets_seen, digit); |
|||
*(dest++) = CORK_UINT16_HOST_TO_BIG(digit); |
|||
hextets_seen++; |
|||
} else if (CORK_UNLIKELY(another_required)) { |
|||
goto parse_error; |
|||
} |
|||
|
|||
if (!double_colon_allowed) { |
|||
/* We've seen a double-colon, so use 0000 for any hextets that weren't |
|||
* present. */ |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
char parsed_result[CORK_IPV6_STRING_LENGTH]; |
|||
#endif |
|||
unsigned int after_count = hextets_seen - before_count; |
|||
DEBUG("Saw double-colon; %u hextets before, %u after\n", |
|||
before_count, after_count); |
|||
memset(addr, 0, sizeof(struct cork_ipv6)); |
|||
memcpy(addr, before_double_colon, |
|||
sizeof(uint16_t) * before_count); |
|||
memcpy(&addr->_.u16[8-after_count], after_double_colon, |
|||
sizeof(uint16_t) * after_count); |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
cork_ipv6_to_raw_string(addr, parsed_result); |
|||
DEBUG("\tParsed address: %s\n", parsed_result); |
|||
#endif |
|||
return 0; |
|||
} else if (hextets_seen == 8) { |
|||
/* No double-colon, so we must have exactly eight hextets. */ |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
char parsed_result[CORK_IPV6_STRING_LENGTH]; |
|||
#endif |
|||
DEBUG("No double-colon\n"); |
|||
cork_ipv6_copy(addr, before_double_colon); |
|||
#if CORK_IP_ADDRESS_DEBUG |
|||
cork_ipv6_to_raw_string(addr, parsed_result); |
|||
DEBUG("\tParsed address: %s\n", parsed_result); |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
parse_error: |
|||
DEBUG("parse error\n"); |
|||
cork_parse_error("Invalid IPv6 address: \"%s\"", str); |
|||
return -1; |
|||
} |
|||
|
|||
bool |
|||
cork_ipv6_equal_(const struct cork_ipv6 *addr1, const struct cork_ipv6 *addr2) |
|||
{ |
|||
return cork_ipv6_equal(addr1, addr2); |
|||
} |
|||
|
|||
#define NS_IN6ADDRSZ 16 |
|||
#define NS_INT16SZ 2 |
|||
|
|||
void |
|||
cork_ipv6_to_raw_string(const struct cork_ipv6 *addr, char *dest) |
|||
{ |
|||
const uint8_t *src = addr->_.u8; |
|||
|
|||
/* |
|||
* Note that int32_t and int16_t need only be "at least" large enough |
|||
* to contain a value of the specified size. On some systems, like |
|||
* Crays, there is no such thing as an integer variable with 16 bits. |
|||
* Keep this in mind if you think this function should have been coded |
|||
* to use pointer overlays. All the world's not a VAX. |
|||
*/ |
|||
char *tp; |
|||
struct { int base, len; } best, cur; |
|||
unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; |
|||
int i; |
|||
|
|||
/* |
|||
* Preprocess: |
|||
* Copy the input (bytewise) array into a wordwise array. |
|||
* Find the longest run of 0x00's in src[] for :: shorthanding. |
|||
*/ |
|||
memset(words, '\0', sizeof words); |
|||
for (i = 0; i < NS_IN6ADDRSZ; i++) |
|||
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); |
|||
best.base = -1; |
|||
best.len = 0; |
|||
cur.base = -1; |
|||
cur.len = 0; |
|||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { |
|||
if (words[i] == 0) { |
|||
if (cur.base == -1) |
|||
cur.base = i, cur.len = 1; |
|||
else |
|||
cur.len++; |
|||
} else { |
|||
if (cur.base != -1) { |
|||
if (best.base == -1 || cur.len > best.len) |
|||
best = cur; |
|||
cur.base = -1; |
|||
} |
|||
} |
|||
} |
|||
if (cur.base != -1) { |
|||
if (best.base == -1 || cur.len > best.len) |
|||
best = cur; |
|||
} |
|||
if (best.base != -1 && best.len < 2) |
|||
best.base = -1; |
|||
|
|||
/* |
|||
* Format the result. |
|||
*/ |
|||
tp = dest; |
|||
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { |
|||
/* Are we inside the best run of 0x00's? */ |
|||
if (best.base != -1 && i >= best.base && |
|||
i < (best.base + best.len)) { |
|||
if (i == best.base) |
|||
*tp++ = ':'; |
|||
continue; |
|||
} |
|||
/* Are we following an initial run of 0x00s or any real hex? */ |
|||
if (i != 0) |
|||
*tp++ = ':'; |
|||
/* Is this address an encapsulated IPv4? */ |
|||
if (i == 6 && best.base == 0 && |
|||
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { |
|||
tp += sprintf(tp, "%u.%u.%u.%u", |
|||
src[12], src[13], src[14], src[15]); |
|||
break; |
|||
} |
|||
tp += sprintf(tp, "%x", words[i]); |
|||
} |
|||
/* Was it a trailing run of 0x00's? */ |
|||
if (best.base != -1 && (best.base + best.len) == |
|||
(NS_IN6ADDRSZ / NS_INT16SZ)) |
|||
*tp++ = ':'; |
|||
*tp++ = '\0'; |
|||
} |
|||
|
|||
bool |
|||
cork_ipv6_is_valid_network(const struct cork_ipv6 *addr, |
|||
unsigned int cidr_prefix) |
|||
{ |
|||
uint64_t cidr_mask[2]; |
|||
|
|||
if (cidr_prefix > 128) { |
|||
return false; |
|||
} else if (cidr_prefix == 128) { |
|||
/* This handles undefined behavior for overflow bit shifts. */ |
|||
cidr_mask[0] = cidr_mask[1] = 0; |
|||
} else if (cidr_prefix == 64) { |
|||
/* This handles undefined behavior for overflow bit shifts. */ |
|||
cidr_mask[0] = 0; |
|||
cidr_mask[1] = UINT64_C(0xffffffffffffffff); |
|||
} else if (cidr_prefix > 64) { |
|||
cidr_mask[0] = 0; |
|||
cidr_mask[1] = UINT64_C(0xffffffffffffffff) >> (cidr_prefix-64); |
|||
} else { |
|||
cidr_mask[0] = UINT64_C(0xffffffffffffffff) >> cidr_prefix; |
|||
cidr_mask[1] = UINT64_C(0xffffffffffffffff); |
|||
} |
|||
|
|||
return (CORK_UINT64_BIG_TO_HOST(addr->_.u64[0] & cidr_mask[0]) == 0) && |
|||
(CORK_UINT64_BIG_TO_HOST(addr->_.u64[1] & cidr_mask[1]) == 0); |
|||
} |
|||
|
|||
|
|||
/*** IP ***/ |
|||
|
|||
void |
|||
cork_ip_from_ipv4_(struct cork_ip *addr, const void *src) |
|||
{ |
|||
cork_ip_from_ipv4(addr, src); |
|||
} |
|||
|
|||
void |
|||
cork_ip_from_ipv6_(struct cork_ip *addr, const void *src) |
|||
{ |
|||
cork_ip_from_ipv6(addr, src); |
|||
} |
|||
|
|||
int |
|||
cork_ip_init(struct cork_ip *addr, const char *str) |
|||
{ |
|||
int rc; |
|||
|
|||
/* Try IPv4 first */ |
|||
rc = cork_ipv4_init(&addr->ip.v4, str); |
|||
if (rc == 0) { |
|||
/* successful parse */ |
|||
addr->version = 4; |
|||
return 0; |
|||
} |
|||
|
|||
/* Then try IPv6 */ |
|||
cork_error_clear(); |
|||
rc = cork_ipv6_init(&addr->ip.v6, str); |
|||
if (rc == 0) { |
|||
/* successful parse */ |
|||
addr->version = 6; |
|||
return 0; |
|||
} |
|||
|
|||
/* Parse error for both address types */ |
|||
cork_parse_error("Invalid IP address: \"%s\"", str); |
|||
return -1; |
|||
} |
|||
|
|||
bool |
|||
cork_ip_equal_(const struct cork_ip *addr1, const struct cork_ip *addr2) |
|||
{ |
|||
return cork_ip_equal(addr1, addr2); |
|||
} |
|||
|
|||
void |
|||
cork_ip_to_raw_string(const struct cork_ip *addr, char *dest) |
|||
{ |
|||
switch (addr->version) { |
|||
case 4: |
|||
cork_ipv4_to_raw_string(&addr->ip.v4, dest); |
|||
return; |
|||
|
|||
case 6: |
|||
cork_ipv6_to_raw_string(&addr->ip.v6, dest); |
|||
return; |
|||
|
|||
default: |
|||
strncpy(dest, "<INVALID>", CORK_IP_STRING_LENGTH); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
bool |
|||
cork_ip_is_valid_network(const struct cork_ip *addr, unsigned int cidr_prefix) |
|||
{ |
|||
switch (addr->version) { |
|||
case 4: |
|||
return cork_ipv4_is_valid_network(&addr->ip.v4, cidr_prefix); |
|||
case 6: |
|||
return cork_ipv6_is_valid_network(&addr->ip.v6, cidr_prefix); |
|||
default: |
|||
return false; |
|||
} |
|||
} |
@ -0,0 +1,179 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2012-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <assert.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include "libcork/core/callbacks.h" |
|||
#include "libcork/core/mempool.h" |
|||
#include "libcork/core/types.h" |
|||
#include "libcork/helpers/errors.h" |
|||
|
|||
|
|||
#if !defined(CORK_DEBUG_MEMPOOL) |
|||
#define CORK_DEBUG_MEMPOOL 0 |
|||
#endif |
|||
|
|||
#if CORK_DEBUG_MEMPOOL |
|||
#include <stdio.h> |
|||
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) |
|||
#else |
|||
#define DEBUG(...) /* no debug messages */ |
|||
#endif |
|||
|
|||
|
|||
|
|||
struct cork_mempool { |
|||
size_t element_size; |
|||
size_t block_size; |
|||
struct cork_mempool_object *free_list; |
|||
/* The number of objects that have been given out by |
|||
* cork_mempool_new but not returned via cork_mempool_free. */ |
|||
size_t allocated_count; |
|||
struct cork_mempool_block *blocks; |
|||
|
|||
void *user_data; |
|||
cork_free_f free_user_data; |
|||
cork_init_f init_object; |
|||
cork_done_f done_object; |
|||
}; |
|||
|
|||
struct cork_mempool_object { |
|||
/* When this object is unclaimed, it will be in the cork_mempool |
|||
* object's free_list using this pointer. */ |
|||
struct cork_mempool_object *next_free; |
|||
}; |
|||
|
|||
struct cork_mempool_block { |
|||
struct cork_mempool_block *next_block; |
|||
}; |
|||
|
|||
#define cork_mempool_object_size(mp) \ |
|||
(sizeof(struct cork_mempool_object) + (mp)->element_size) |
|||
|
|||
#define cork_mempool_get_header(obj) \ |
|||
(((struct cork_mempool_object *) (obj)) - 1) |
|||
|
|||
#define cork_mempool_get_object(hdr) \ |
|||
((void *) (((struct cork_mempool_object *) (hdr)) + 1)) |
|||
|
|||
|
|||
struct cork_mempool * |
|||
cork_mempool_new_size_ex(size_t element_size, size_t block_size) |
|||
{ |
|||
struct cork_mempool *mp = cork_new(struct cork_mempool); |
|||
mp->element_size = element_size; |
|||
mp->block_size = block_size; |
|||
mp->free_list = NULL; |
|||
mp->allocated_count = 0; |
|||
mp->blocks = NULL; |
|||
mp->user_data = NULL; |
|||
mp->free_user_data = NULL; |
|||
mp->init_object = NULL; |
|||
mp->done_object = NULL; |
|||
return mp; |
|||
} |
|||
|
|||
void |
|||
cork_mempool_free(struct cork_mempool *mp) |
|||
{ |
|||
struct cork_mempool_block *curr; |
|||
assert(mp->allocated_count == 0); |
|||
|
|||
if (mp->done_object != NULL) { |
|||
struct cork_mempool_object *obj; |
|||
for (obj = mp->free_list; obj != NULL; obj = obj->next_free) { |
|||
mp->done_object |
|||
(mp->user_data, cork_mempool_get_object(obj)); |
|||
} |
|||
} |
|||
|
|||
for (curr = mp->blocks; curr != NULL; ) { |
|||
struct cork_mempool_block *next = curr->next_block; |
|||
free(curr); |
|||
/* Do this here instead of in the for statement to avoid |
|||
* accessing the just-freed block. */ |
|||
curr = next; |
|||
} |
|||
|
|||
cork_free_user_data(mp); |
|||
free(mp); |
|||
} |
|||
|
|||
|
|||
void |
|||
cork_mempool_set_callbacks(struct cork_mempool *mp, |
|||
void *user_data, cork_free_f free_user_data, |
|||
cork_init_f init_object, |
|||
cork_done_f done_object) |
|||
{ |
|||
cork_free_user_data(mp); |
|||
mp->user_data = user_data; |
|||
mp->free_user_data = free_user_data; |
|||
mp->init_object = init_object; |
|||
mp->done_object = done_object; |
|||
} |
|||
|
|||
|
|||
/* If this function succeeds, then we guarantee that there will be at |
|||
* least one object in mp->free_list. */ |
|||
static void |
|||
cork_mempool_new_block(struct cork_mempool *mp) |
|||
{ |
|||
/* Allocate the new block and add it to mp's block list. */ |
|||
struct cork_mempool_block *block; |
|||
void *vblock; |
|||
DEBUG("Allocating new %zu-byte block\n", mp->block_size); |
|||
block = cork_malloc(mp->block_size); |
|||
block->next_block = mp->blocks; |
|||
mp->blocks = block; |
|||
vblock = block; |
|||
|
|||
/* Divide the block's memory region into a bunch of objects. */ |
|||
size_t index = sizeof(struct cork_mempool_block); |
|||
for (index = sizeof(struct cork_mempool_block); |
|||
(index + cork_mempool_object_size(mp)) <= mp->block_size; |
|||
index += cork_mempool_object_size(mp)) { |
|||
struct cork_mempool_object *obj = vblock + index; |
|||
DEBUG(" New object at %p[%p]\n", cork_mempool_get_object(obj), obj); |
|||
if (mp->init_object != NULL) { |
|||
mp->init_object |
|||
(mp->user_data, cork_mempool_get_object(obj)); |
|||
} |
|||
obj->next_free = mp->free_list; |
|||
mp->free_list = obj; |
|||
} |
|||
} |
|||
|
|||
void * |
|||
cork_mempool_new_object(struct cork_mempool *mp) |
|||
{ |
|||
struct cork_mempool_object *obj; |
|||
void *ptr; |
|||
|
|||
if (CORK_UNLIKELY(mp->free_list == NULL)) { |
|||
cork_mempool_new_block(mp); |
|||
} |
|||
|
|||
obj = mp->free_list; |
|||
mp->free_list = obj->next_free; |
|||
mp->allocated_count++; |
|||
ptr = cork_mempool_get_object(obj); |
|||
return ptr; |
|||
} |
|||
|
|||
void |
|||
cork_mempool_free_object(struct cork_mempool *mp, void *ptr) |
|||
{ |
|||
struct cork_mempool_object *obj = cork_mempool_get_header(ptr); |
|||
DEBUG("Returning %p[%p] to memory pool\n", ptr, obj); |
|||
obj->next_free = mp->free_list; |
|||
mp->free_list = obj; |
|||
mp->allocated_count--; |
|||
} |
@ -0,0 +1,162 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <string.h> |
|||
#include <time.h> |
|||
#include <sys/time.h> |
|||
|
|||
#include "libcork/core/timestamp.h" |
|||
#include "libcork/core/types.h" |
|||
#include "libcork/helpers/errors.h" |
|||
|
|||
void |
|||
cork_timestamp_init_now(cork_timestamp *ts) |
|||
{ |
|||
struct timeval tp; |
|||
gettimeofday(&tp, NULL); |
|||
cork_timestamp_init_usec(ts, tp.tv_sec, tp.tv_usec); |
|||
} |
|||
|
|||
|
|||
#define is_digit(ch) ((ch) >= '0' && (ch) <= '9') |
|||
|
|||
static uint64_t |
|||
power_of_10(unsigned int width) |
|||
{ |
|||
uint64_t accumulator = 10; |
|||
uint64_t result = 1; |
|||
while (width != 0) { |
|||
if ((width % 2) == 1) { |
|||
result *= accumulator; |
|||
width--; |
|||
} |
|||
accumulator *= accumulator; |
|||
width /= 2; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
static int |
|||
append_fractional(const cork_timestamp ts, unsigned int width, |
|||
struct cork_buffer *dest) |
|||
{ |
|||
if (CORK_UNLIKELY(width == 0 || width > 9)) { |
|||
cork_error_set_printf |
|||
(EINVAL, |
|||
"Invalid width %u for fractional cork_timestamp", width); |
|||
return -1; |
|||
} else { |
|||
uint64_t denom = power_of_10(width); |
|||
uint64_t frac = cork_timestamp_gsec_to_units(ts, denom); |
|||
cork_buffer_append_printf(dest, "%0*" PRIu64, width, frac); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
static int |
|||
cork_timestamp_format_parts(const cork_timestamp ts, struct tm *tm, |
|||
const char *format, struct cork_buffer *dest) |
|||
{ |
|||
const char *next_percent; |
|||
|
|||
while ((next_percent = strchr(format, '%')) != NULL) { |
|||
const char *spec = next_percent + 1; |
|||
unsigned int width = 0; |
|||
|
|||
/* First append any text in between the previous format specifier and |
|||
* this one. */ |
|||
cork_buffer_append(dest, format, next_percent - format); |
|||
|
|||
/* Then parse the format specifier */ |
|||
while (is_digit(*spec)) { |
|||
width *= 10; |
|||
width += (*spec++ - '0'); |
|||
} |
|||
|
|||
switch (*spec) { |
|||
case '\0': |
|||
cork_error_set_string |
|||
(EINVAL, |
|||
"Trailing %% at end of cork_timestamp format string"); |
|||
return -1; |
|||
|
|||
case '%': |
|||
cork_buffer_append(dest, "%", 1); |
|||
break; |
|||
|
|||
case 'Y': |
|||
cork_buffer_append_printf(dest, "%04d", tm->tm_year + 1900); |
|||
break; |
|||
|
|||
case 'm': |
|||
cork_buffer_append_printf(dest, "%02d", tm->tm_mon + 1); |
|||
break; |
|||
|
|||
case 'd': |
|||
cork_buffer_append_printf(dest, "%02d", tm->tm_mday); |
|||
break; |
|||
|
|||
case 'H': |
|||
cork_buffer_append_printf(dest, "%02d", tm->tm_hour); |
|||
break; |
|||
|
|||
case 'M': |
|||
cork_buffer_append_printf(dest, "%02d", tm->tm_min); |
|||
break; |
|||
|
|||
case 'S': |
|||
cork_buffer_append_printf(dest, "%02d", tm->tm_sec); |
|||
break; |
|||
|
|||
case 's': |
|||
cork_buffer_append_printf |
|||
(dest, "%" PRIu32, cork_timestamp_sec(ts)); |
|||
break; |
|||
|
|||
case 'f': |
|||
rii_check(append_fractional(ts, width, dest)); |
|||
break; |
|||
|
|||
default: |
|||
cork_error_set_printf |
|||
(EINVAL, |
|||
"Unknown cork_timestamp format specifier %%%c", *spec); |
|||
return -1; |
|||
} |
|||
|
|||
format = spec + 1; |
|||
} |
|||
|
|||
/* When we fall through, there is some additional content after the final |
|||
* format specifier. */ |
|||
cork_buffer_append_string(dest, format); |
|||
return 0; |
|||
} |
|||
|
|||
int |
|||
cork_timestamp_format_utc(const cork_timestamp ts, const char *format, |
|||
struct cork_buffer *dest) |
|||
{ |
|||
time_t clock; |
|||
struct tm tm; |
|||
clock = cork_timestamp_sec(ts); |
|||
gmtime_r(&clock, &tm); |
|||
return cork_timestamp_format_parts(ts, &tm, format, dest); |
|||
} |
|||
|
|||
int |
|||
cork_timestamp_format_local(const cork_timestamp ts, const char *format, |
|||
struct cork_buffer *dest) |
|||
{ |
|||
time_t clock; |
|||
struct tm tm; |
|||
clock = cork_timestamp_sec(ts); |
|||
localtime_r(&clock, &tm); |
|||
return cork_timestamp_format_parts(ts, &tm, format, dest); |
|||
} |
@ -0,0 +1,85 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#include <string.h> |
|||
#include <stdio.h> |
|||
|
|||
#include "libcork/core/types.h" |
|||
#include "libcork/core/u128.h" |
|||
|
|||
|
|||
/* From http://stackoverflow.com/questions/8023414/how-to-convert-a-128-bit-integer-to-a-decimal-ascii-string-in-c */ |
|||
|
|||
const char * |
|||
cork_u128_to_decimal(char *dest, cork_u128 val) |
|||
{ |
|||
uint32_t n[4]; |
|||
char *s = dest; |
|||
char *p = dest; |
|||
unsigned int i; |
|||
|
|||
/* This algorithm assumes that n[3] is the MSW. */ |
|||
n[3] = cork_u128_be32(val, 0); |
|||
n[2] = cork_u128_be32(val, 1); |
|||
n[1] = cork_u128_be32(val, 2); |
|||
n[0] = cork_u128_be32(val, 3); |
|||
|
|||
memset(s, '0', CORK_U128_DECIMAL_LENGTH - 1); |
|||
s[CORK_U128_DECIMAL_LENGTH - 1] = '\0'; |
|||
|
|||
for (i = 0; i < 128; i++) { |
|||
unsigned int j; |
|||
unsigned int carry; |
|||
|
|||
carry = (n[3] >= 0x80000000); |
|||
/* Shift n[] left, doubling it */ |
|||
n[3] = ((n[3] << 1) & 0xFFFFFFFF) + (n[2] >= 0x80000000); |
|||
n[2] = ((n[2] << 1) & 0xFFFFFFFF) + (n[1] >= 0x80000000); |
|||
n[1] = ((n[1] << 1) & 0xFFFFFFFF) + (n[0] >= 0x80000000); |
|||
n[0] = ((n[0] << 1) & 0xFFFFFFFF); |
|||
|
|||
/* Add s[] to itself in decimal, doubling it */ |
|||
for (j = CORK_U128_DECIMAL_LENGTH - 1; j-- > 0; ) { |
|||
s[j] += s[j] - '0' + carry; |
|||
carry = (s[j] > '9'); |
|||
if (carry) { |
|||
s[j] -= 10; |
|||
} |
|||
} |
|||
} |
|||
|
|||
while ((p[0] == '0') && (p < &s[CORK_U128_DECIMAL_LENGTH - 2])) { |
|||
p++; |
|||
} |
|||
|
|||
return p; |
|||
} |
|||
|
|||
|
|||
const char * |
|||
cork_u128_to_hex(char *buf, cork_u128 val) |
|||
{ |
|||
uint64_t hi = val._.be64.hi; |
|||
uint64_t lo = val._.be64.lo; |
|||
if (hi == 0) { |
|||
snprintf(buf, CORK_U128_HEX_LENGTH, "%" PRIx64, lo); |
|||
} else { |
|||
snprintf(buf, CORK_U128_HEX_LENGTH, "%" PRIx64 "%016" PRIx64, hi, lo); |
|||
} |
|||
return buf; |
|||
} |
|||
|
|||
const char * |
|||
cork_u128_to_padded_hex(char *buf, cork_u128 val) |
|||
{ |
|||
uint64_t hi = val._.be64.hi; |
|||
uint64_t lo = val._.be64.lo; |
|||
snprintf(buf, CORK_U128_HEX_LENGTH, "%016" PRIx64 "%016" PRIx64, hi, lo); |
|||
return buf; |
|||
} |
@ -0,0 +1,75 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2012, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_ALLOCATOR_H |
|||
#define LIBCORK_CORE_ALLOCATOR_H |
|||
|
|||
#include <stdlib.h> |
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/attributes.h> |
|||
#include <libcork/core/error.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Recoverable |
|||
*/ |
|||
|
|||
#define cork_xmalloc malloc |
|||
#define cork_xcalloc calloc |
|||
#define cork_xfree free |
|||
|
|||
#if CORK_HAVE_REALLOCF |
|||
#define cork_xrealloc reallocf |
|||
#else |
|||
CORK_API void * |
|||
cork_xrealloc(void *ptr, size_t new_size) CORK_ATTR_MALLOC; |
|||
#endif |
|||
|
|||
/* type-based macros */ |
|||
#define cork_xnew(type) ((type *) cork_xmalloc(sizeof(type))) |
|||
|
|||
/* string-related functions */ |
|||
|
|||
CORK_API const char * |
|||
cork_xstrdup(const char *str); |
|||
|
|||
CORK_API const char * |
|||
cork_xstrndup(const char *str, size_t size); |
|||
|
|||
CORK_API void |
|||
cork_strfree(const char *str); |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Abort on failure |
|||
*/ |
|||
|
|||
CORK_API void * |
|||
cork_malloc(size_t size) CORK_ATTR_MALLOC; |
|||
|
|||
CORK_API void * |
|||
cork_calloc(size_t count, size_t size) CORK_ATTR_MALLOC; |
|||
|
|||
CORK_API void * |
|||
cork_realloc(void *ptr, size_t new_size) CORK_ATTR_MALLOC; |
|||
|
|||
CORK_API const char * |
|||
cork_strdup(const char *src) CORK_ATTR_MALLOC; |
|||
|
|||
CORK_API const char * |
|||
cork_strndup(const char *src, size_t size) CORK_ATTR_MALLOC; |
|||
|
|||
#define cork_new(type) \ |
|||
cork_malloc(sizeof(type)) |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_ALLOCATOR_H */ |
@ -0,0 +1,25 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2012, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_API_H |
|||
#define LIBCORK_CORE_API_H |
|||
|
|||
#include <libcork/core/attributes.h> |
|||
|
|||
/* If you're using libcork as a shared library, you don't need to do anything |
|||
* special; the following will automatically set things up so that libcork's |
|||
* public symbols are imported from the library. When we build the shared |
|||
* library, we define this ourselves to export the symbols. */ |
|||
|
|||
#if !defined(CORK_API) |
|||
#define CORK_API CORK_IMPORT |
|||
#endif |
|||
|
|||
#endif /* LIBCORK_CORE_API_H */ |
@ -0,0 +1,172 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_ATTRIBUTES_H |
|||
#define LIBCORK_CORE_ATTRIBUTES_H |
|||
|
|||
#include <libcork/config.h> |
|||
|
|||
|
|||
/* |
|||
* Declare a “const” function. |
|||
* |
|||
* A const function is one whose return value depends only on its |
|||
* parameters. This is slightly more strict than a “pure” function; a |
|||
* const function is not allowed to read from global variables, whereas |
|||
* a pure function is. |
|||
* |
|||
* int square(int x) CORK_ATTR_CONST; |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_CONST __attribute__((const)) |
|||
#else |
|||
#define CORK_ATTR_CONST |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare a “pure” function. |
|||
* |
|||
* A pure function is one whose return value depends only on its |
|||
* parameters, and global variables. |
|||
* |
|||
* int square(int x) CORK_ATTR_PURE; |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_PURE __attribute__((pure)) |
|||
#else |
|||
#define CORK_ATTR_PURE |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare that a function returns a newly allocated pointer. |
|||
* |
|||
* The compiler can use this information to generate more accurate |
|||
* aliasing information, since it can infer that the result of the |
|||
* function cannot alias any other existing pointer. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_MALLOC __attribute__((malloc)) |
|||
#else |
|||
#define CORK_ATTR_MALLOC |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare that a function shouldn't be inlined. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_NOINLINE __attribute__((noinline)) |
|||
#else |
|||
#define CORK_ATTR_NOINLINE |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare an entity that isn't used. |
|||
* |
|||
* This lets you keep -Wall activated in several cases where you're |
|||
* obligated to define something that you don't intend to use. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_UNUSED __attribute__((unused)) |
|||
#else |
|||
#define CORK_ATTR_UNUSED |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare a function that takes in printf-like parameters. |
|||
* |
|||
* When the compiler supports this attribute, it will check the format |
|||
* string, and the following arguments, to make sure that they match. |
|||
* format_index and args_index are 1-based. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_PRINTF(format_index, args_index) \ |
|||
__attribute__((format(printf, format_index, args_index))) |
|||
#else |
|||
#define CORK_ATTR_PRINTF(format_index, args_index) |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare a var-arg function whose last parameter must be a NULL |
|||
* sentinel value. |
|||
* |
|||
* When the compiler supports this attribute, it will check the actual |
|||
* parameters whenever this function is called, and ensure that the last |
|||
* parameter is a @c NULL. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_ATTR_SENTINEL __attribute__((sentinel)) |
|||
#else |
|||
#define CORK_ATTR_SENTINEL |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare that a boolean expression is likely to be true or false. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_LIKELY(expr) __builtin_expect((expr), 1) |
|||
#define CORK_UNLIKELY(expr) __builtin_expect((expr), 0) |
|||
#else |
|||
#define CORK_LIKELY(expr) (expr) |
|||
#define CORK_UNLIKELY(expr) (expr) |
|||
#endif |
|||
|
|||
/* |
|||
* Declare that a function is part of the current library's public API, or that |
|||
* it's internal to the current library. |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_EXPORT __attribute__((visibility("default"))) |
|||
#define CORK_IMPORT __attribute__((visibility("default"))) |
|||
#define CORK_LOCAL __attribute__((visibility("hidden"))) |
|||
#else |
|||
#define CORK_EXPORT |
|||
#define CORK_IMPORT |
|||
#define CORK_LOCAL |
|||
#endif |
|||
|
|||
|
|||
/* |
|||
* Declare a static function that should automatically be called at program |
|||
* startup. |
|||
*/ |
|||
|
|||
/* TODO: When we implement a full Windows port, [1] describes how best to |
|||
* implement an initialization function under Visual Studio. |
|||
* |
|||
* [1] http://stackoverflow.com/questions/1113409/attribute-constructor-equivalent-in-vc |
|||
*/ |
|||
|
|||
#if CORK_CONFIG_HAVE_GCC_ATTRIBUTES |
|||
#define CORK_INITIALIZER(name) \ |
|||
__attribute__((constructor)) \ |
|||
static void \ |
|||
name(void) |
|||
#else |
|||
#error "Don't know how to implement initialization functions of this platform" |
|||
#endif |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_ATTRIBUTES_H */ |
@ -0,0 +1,186 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_BYTE_ORDER_H |
|||
#define LIBCORK_CORE_BYTE_ORDER_H |
|||
|
|||
|
|||
#include <libcork/config.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
/* Constants to represent big endianness and little endianness */ |
|||
#define CORK_BIG_ENDIAN 4321 |
|||
#define CORK_LITTLE_ENDIAN 1234 |
|||
|
|||
/* Whether the current host is big- or little-endian. HOST gives us the |
|||
* current system's endianness; OTHER gives the opposite endianness. |
|||
* The _NAME macros can be used in debugging messages and other |
|||
* human-readable output. |
|||
* |
|||
* Note that we actually detect the endianness in the various header |
|||
* files in the libcork/config directory, since we want to keep |
|||
* everything detection-related separated out from what we define based |
|||
* on that detection. */ |
|||
|
|||
#if CORK_CONFIG_IS_BIG_ENDIAN |
|||
#define CORK_HOST_ENDIANNESS CORK_BIG_ENDIAN |
|||
#define CORK_OTHER_ENDIANNESS CORK_LITTLE_ENDIAN |
|||
#define CORK_HOST_ENDIANNESS_NAME "big" |
|||
#define CORK_OTHER_ENDIANNESS_NAME "little" |
|||
|
|||
#elif CORK_CONFIG_IS_LITTLE_ENDIAN |
|||
#define CORK_HOST_ENDIANNESS CORK_LITTLE_ENDIAN |
|||
#define CORK_OTHER_ENDIANNESS CORK_BIG_ENDIAN |
|||
#define CORK_HOST_ENDIANNESS_NAME "little" |
|||
#define CORK_OTHER_ENDIANNESS_NAME "big" |
|||
|
|||
#else |
|||
#error "Unknown endianness" |
|||
#endif |
|||
|
|||
|
|||
/* Returns the byte-swapped version an integer, regardless of the |
|||
* underlying endianness. |
|||
* |
|||
* These macros only require an rvalue as their parameter (which can |
|||
* therefore be any arbitrary expression), and they don't modify the |
|||
* original contents if it happens to be a variable. */ |
|||
|
|||
#define CORK_SWAP_UINT16(__u16) \ |
|||
(((((uint16_t) __u16) & 0xff00u) >> 8) | \ |
|||
((((uint16_t) __u16) & 0x00ffu) << 8)) |
|||
|
|||
#define CORK_SWAP_UINT32(__u32) \ |
|||
(((((uint32_t) __u32) & 0xff000000u) >> 24) | \ |
|||
((((uint32_t) __u32) & 0x00ff0000u) >> 8) | \ |
|||
((((uint32_t) __u32) & 0x0000ff00u) << 8) | \ |
|||
((((uint32_t) __u32) & 0x000000ffu) << 24)) |
|||
|
|||
#define CORK_SWAP_UINT64(__u64) \ |
|||
(((((uint64_t) __u64) & UINT64_C(0xff00000000000000)) >> 56) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x00ff000000000000)) >> 40) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x0000ff0000000000)) >> 24) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x000000ff00000000)) >> 8) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x00000000ff000000)) << 8) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x0000000000ff0000)) << 24) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x000000000000ff00)) << 40) | \ |
|||
((((uint64_t) __u64) & UINT64_C(0x00000000000000ff)) << 56)) |
|||
|
|||
/* Bytes-swaps an integer variable in place. |
|||
* |
|||
* These macros require an lvalue as their parameter; the contents of |
|||
* this variable will be modified by the macro. */ |
|||
|
|||
#define CORK_SWAP_IN_PLACE_UINT16(__u16) \ |
|||
do { \ |
|||
(__u16) = CORK_SWAP_UINT16(__u16); \ |
|||
} while (0) |
|||
|
|||
#define CORK_SWAP_IN_PLACE_UINT32(__u32) \ |
|||
do { \ |
|||
(__u32) = CORK_SWAP_UINT32(__u32); \ |
|||
} while (0) |
|||
|
|||
#define CORK_SWAP_IN_PLACE_UINT64(__u64) \ |
|||
do { \ |
|||
(__u64) = CORK_SWAP_UINT64(__u64); \ |
|||
} while (0) |
|||
|
|||
|
|||
/* |
|||
* A slew of swapping macros whose operation depends on the endianness |
|||
* of the current system: |
|||
* |
|||
* uint16_t CORK_UINT16_BIG_TO_HOST(u16) |
|||
* uint32_t CORK_UINT32_BIG_TO_HOST(u32) |
|||
* uint64_t CORK_UINT64_BIG_TO_HOST(u64) |
|||
* uint16_t CORK_UINT16_LITTLE_TO_HOST(u16) |
|||
* uint32_t CORK_UINT32_LITTLE_TO_HOST(u32) |
|||
* uint64_t CORK_UINT64_LITTLE_TO_HOST(u64) |
|||
* void CORK_UINT16_BIG_TO_HOST_IN_PLACE(&u16) |
|||
* void CORK_UINT32_BIG_TO_HOST_IN_PLACE(&u32) |
|||
* void CORK_UINT64_BIG_TO_HOST_IN_PLACE(&u64) |
|||
* void CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(&u16) |
|||
* void CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(&u32) |
|||
* void CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(&u64) |
|||
* |
|||
* uint16_t CORK_UINT16_HOST_TO_BIG(u16) |
|||
* uint32_t CORK_UINT32_HOST_TO_BIG(u32) |
|||
* uint64_t CORK_UINT64_HOST_TO_BIG(u64) |
|||
* uint16_t CORK_UINT16_HOST_TO_LITTLE(u16) |
|||
* uint32_t CORK_UINT32_HOST_TO_LITTLE(u32) |
|||
* uint64_t CORK_UINT64_HOST_TO_LITTLE(u64) |
|||
* void CORK_UINT16_HOST_TO_BIG_IN_PLACE(&u16) |
|||
* void CORK_UINT32_HOST_TO_BIG_IN_PLACE(&u32) |
|||
* void CORK_UINT64_HOST_TO_BIG_IN_PLACE(&u64) |
|||
* void CORK_UINT16_HOST_TO_LITTLE_IN_PLACE(&u16) |
|||
* void CORK_UINT32_HOST_TO_LITTLE_IN_PLACE(&u32) |
|||
* void CORK_UINT64_HOST_TO_LITTLE_IN_PLACE(&u64) |
|||
*/ |
|||
|
|||
#if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN |
|||
|
|||
#define CORK_UINT16_BIG_TO_HOST(__u16) (__u16) /* nothing to do */ |
|||
#define CORK_UINT16_LITTLE_TO_HOST(__u16) CORK_SWAP_UINT16(__u16) |
|||
|
|||
#define CORK_UINT32_BIG_TO_HOST(__u32) (__u32) /* nothing to do */ |
|||
#define CORK_UINT32_LITTLE_TO_HOST(__u32) CORK_SWAP_UINT32(__u32) |
|||
|
|||
#define CORK_UINT64_BIG_TO_HOST(__u64) (__u64) /* nothing to do */ |
|||
#define CORK_UINT64_LITTLE_TO_HOST(__u64) CORK_SWAP_UINT64(__u64) |
|||
|
|||
#define CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) /* nothing to do */ |
|||
#define CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) CORK_SWAP_IN_PLACE_UINT16(__u16) |
|||
|
|||
#define CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) /* nothing to do */ |
|||
#define CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) CORK_SWAP_IN_PLACE_UINT32(__u32) |
|||
|
|||
#define CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) /* nothing to do */ |
|||
#define CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) CORK_SWAP_IN_PLACE_UINT64(__u64) |
|||
|
|||
#elif CORK_HOST_ENDIANNESS == CORK_LITTLE_ENDIAN |
|||
|
|||
#define CORK_UINT16_BIG_TO_HOST(__u16) CORK_SWAP_UINT16(__u16) |
|||
#define CORK_UINT16_LITTLE_TO_HOST(__u16) (__u16) /* nothing to do */ |
|||
|
|||
#define CORK_UINT32_BIG_TO_HOST(__u32) CORK_SWAP_UINT32(__u32) |
|||
#define CORK_UINT32_LITTLE_TO_HOST(__u32) (__u32) /* nothing to do */ |
|||
|
|||
#define CORK_UINT64_BIG_TO_HOST(__u64) CORK_SWAP_UINT64(__u64) |
|||
#define CORK_UINT64_LITTLE_TO_HOST(__u64) (__u64) /* nothing to do */ |
|||
|
|||
#define CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) CORK_SWAP_IN_PLACE_UINT16(__u16) |
|||
#define CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) /* nothing to do */ |
|||
|
|||
#define CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) CORK_SWAP_IN_PLACE_UINT32(__u32) |
|||
#define CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) /* nothing to do */ |
|||
|
|||
#define CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) CORK_SWAP_IN_PLACE_UINT64(__u64) |
|||
#define CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) /* nothing to do */ |
|||
|
|||
#endif |
|||
|
|||
|
|||
#define CORK_UINT16_HOST_TO_BIG(__u16) CORK_UINT16_BIG_TO_HOST(__u16) |
|||
#define CORK_UINT32_HOST_TO_BIG(__u32) CORK_UINT32_BIG_TO_HOST(__u32) |
|||
#define CORK_UINT64_HOST_TO_BIG(__u64) CORK_UINT64_BIG_TO_HOST(__u64) |
|||
#define CORK_UINT16_HOST_TO_LITTLE(__u16) CORK_UINT16_LITTLE_TO_HOST(__u16) |
|||
#define CORK_UINT32_HOST_TO_LITTLE(__u32) CORK_UINT32_LITTLE_TO_HOST(__u32) |
|||
#define CORK_UINT64_HOST_TO_LITTLE(__u64) CORK_UINT64_LITTLE_TO_HOST(__u64) |
|||
#define CORK_UINT16_HOST_TO_BIG_IN_PLACE(__u16) CORK_UINT16_BIG_TO_HOST_IN_PLACE(__u16) |
|||
#define CORK_UINT32_HOST_TO_BIG_IN_PLACE(__u32) CORK_UINT32_BIG_TO_HOST_IN_PLACE(__u32) |
|||
#define CORK_UINT64_HOST_TO_BIG_IN_PLACE(__u64) CORK_UINT64_BIG_TO_HOST_IN_PLACE(__u64) |
|||
#define CORK_UINT16_HOST_TO_LITTLE_IN_PLACE(__u16) CORK_UINT16_LITTLE_TO_HOST_IN_PLACE(__u16) |
|||
#define CORK_UINT32_HOST_TO_LITTLE_IN_PLACE(__u32) CORK_UINT32_LITTLE_TO_HOST_IN_PLACE(__u32) |
|||
#define CORK_UINT64_HOST_TO_LITTLE_IN_PLACE(__u64) CORK_UINT64_LITTLE_TO_HOST_IN_PLACE(__u64) |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_BYTE_ORDER_H */ |
@ -0,0 +1,43 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_CALLBACKS_H |
|||
#define LIBCORK_CORE_CALLBACKS_H |
|||
|
|||
|
|||
#include <libcork/core/hash.h> |
|||
|
|||
|
|||
typedef int |
|||
(*cork_copy_f)(void *user_data, void *dest, const void *src); |
|||
|
|||
typedef void |
|||
(*cork_done_f)(void *user_data, void *value); |
|||
|
|||
typedef void |
|||
(*cork_free_f)(void *value); |
|||
|
|||
typedef cork_hash |
|||
(*cork_hash_f)(void *user_data, const void *value); |
|||
|
|||
typedef bool |
|||
(*cork_equals_f)(void *user_data, const void *value1, const void *value2); |
|||
|
|||
typedef void |
|||
(*cork_init_f)(void *user_data, void *value); |
|||
|
|||
#define cork_free_user_data(parent) \ |
|||
((parent)->free_user_data == NULL? (void) 0: \ |
|||
(parent)->free_user_data((parent)->user_data)) |
|||
|
|||
typedef void * |
|||
(*cork_new_f)(void *user_data); |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_CALLBACKS_H */ |
@ -0,0 +1,139 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_ERROR_H |
|||
#define LIBCORK_CORE_ERROR_H |
|||
|
|||
#include <errno.h> |
|||
#include <stdarg.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/attributes.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
/* Should be a hash of a string representing the error code. */ |
|||
typedef uint32_t cork_error; |
|||
|
|||
/* An error code that represents “no error”. */ |
|||
#define CORK_ERROR_NONE ((cork_error) 0) |
|||
|
|||
CORK_API bool |
|||
cork_error_occurred(void); |
|||
|
|||
CORK_API cork_error |
|||
cork_error_code(void); |
|||
|
|||
CORK_API const char * |
|||
cork_error_message(void); |
|||
|
|||
|
|||
CORK_API void |
|||
cork_error_clear(void); |
|||
|
|||
CORK_API void |
|||
cork_error_set_printf(cork_error code, const char *format, ...) |
|||
CORK_ATTR_PRINTF(2,3); |
|||
|
|||
CORK_API void |
|||
cork_error_set_string(cork_error code, const char *str); |
|||
|
|||
CORK_API void |
|||
cork_error_set_vprintf(cork_error code, const char *format, va_list args) |
|||
CORK_ATTR_PRINTF(2,0); |
|||
|
|||
CORK_API void |
|||
cork_error_prefix_printf(const char *format, ...) |
|||
CORK_ATTR_PRINTF(1,2); |
|||
|
|||
CORK_API void |
|||
cork_error_prefix_string(const char *str); |
|||
|
|||
CORK_API void |
|||
cork_error_prefix_vprintf(const char *format, va_list arg) |
|||
CORK_ATTR_PRINTF(1,0); |
|||
|
|||
|
|||
/* deprecated */ |
|||
CORK_API void |
|||
cork_error_set(uint32_t error_class, unsigned int error_code, |
|||
const char *format, ...) |
|||
CORK_ATTR_PRINTF(3,4); |
|||
|
|||
/* deprecated */ |
|||
CORK_API void |
|||
cork_error_prefix(const char *format, ...) |
|||
CORK_ATTR_PRINTF(1,2); |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Built-in errors |
|||
*/ |
|||
|
|||
#define CORK_PARSE_ERROR 0x95dfd3c8 |
|||
#define CORK_REDEFINED 0x171629cb |
|||
#define CORK_UNDEFINED 0xedc3d7d9 |
|||
#define CORK_UNKNOWN_ERROR 0x8cb0880d |
|||
|
|||
#define cork_parse_error(...) \ |
|||
cork_error_set_printf(CORK_PARSE_ERROR, __VA_ARGS__) |
|||
#define cork_redefined(...) \ |
|||
cork_error_set_printf(CORK_REDEFINED, __VA_ARGS__) |
|||
#define cork_undefined(...) \ |
|||
cork_error_set_printf(CORK_UNDEFINED, __VA_ARGS__) |
|||
|
|||
CORK_API void |
|||
cork_system_error_set(void); |
|||
|
|||
CORK_API void |
|||
cork_system_error_set_explicit(int err); |
|||
|
|||
CORK_API void |
|||
cork_unknown_error_set_(const char *location); |
|||
|
|||
#define cork_unknown_error() \ |
|||
cork_unknown_error_set_(__func__) |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* Abort on failure |
|||
*/ |
|||
|
|||
#define cork_abort_(func, file, line, fmt, ...) \ |
|||
do { \ |
|||
fprintf(stderr, fmt "\n in %s (%s:%u)\n", \ |
|||
__VA_ARGS__, (func), (file), (unsigned int) (line)); \ |
|||
abort(); \ |
|||
} while (0) |
|||
|
|||
#define cork_abort(fmt, ...) \ |
|||
cork_abort_(__func__, __FILE__, __LINE__, fmt, __VA_ARGS__) |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static void * |
|||
cork_abort_if_null_(void *ptr, const char *msg, const char *func, |
|||
const char *file, unsigned int line) |
|||
{ |
|||
if (CORK_UNLIKELY(ptr == NULL)) { |
|||
cork_abort_(func, file, line, "%s", msg); |
|||
} else { |
|||
return ptr; |
|||
} |
|||
} |
|||
|
|||
#define cork_abort_if_null(ptr, msg) \ |
|||
(cork_abort_if_null_(ptr, msg, __func__, __FILE__, __LINE__)) |
|||
|
|||
#define cork_unreachable() \ |
|||
cork_abort("%s", "Code should not be reachable") |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_ERROR_H */ |
@ -0,0 +1,67 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_GC_REFCOUNT_H |
|||
#define LIBCORK_GC_REFCOUNT_H |
|||
|
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
struct cork_gc; |
|||
|
|||
/* A callback for recursing through the children of a garbage-collected |
|||
* object. */ |
|||
typedef void |
|||
(*cork_gc_recurser)(struct cork_gc *gc, void *obj, void *ud); |
|||
|
|||
typedef void |
|||
(*cork_gc_free_func)(void *obj); |
|||
|
|||
typedef void |
|||
(*cork_gc_recurse_func)(struct cork_gc *gc, void *self, |
|||
cork_gc_recurser recurser, void *ud); |
|||
|
|||
/* An interface that each garbage-collected object must implement. */ |
|||
struct cork_gc_obj_iface { |
|||
/* Perform additional cleanup; does *NOT* need to deallocate the |
|||
* object itself, or release any child references */ |
|||
cork_gc_free_func free; |
|||
cork_gc_recurse_func recurse; |
|||
}; |
|||
|
|||
|
|||
CORK_API void |
|||
cork_gc_init(void); |
|||
|
|||
CORK_API void |
|||
cork_gc_done(void); |
|||
|
|||
|
|||
CORK_API void * |
|||
cork_gc_alloc(size_t instance_size, struct cork_gc_obj_iface *iface); |
|||
|
|||
#define cork_gc_new_iface(obj_type, iface) \ |
|||
((obj_type *) \ |
|||
(cork_gc_alloc(sizeof(obj_type), (iface)))) |
|||
|
|||
#define cork_gc_new(struct_name) \ |
|||
(cork_gc_new_iface(struct struct_name, &struct_name##__gc)) |
|||
|
|||
|
|||
CORK_API void * |
|||
cork_gc_incref(void *obj); |
|||
|
|||
CORK_API void |
|||
cork_gc_decref(void *obj); |
|||
|
|||
|
|||
#endif /* LIBCORK_GC_REFCOUNT_H */ |
@ -0,0 +1,356 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_HASH_H |
|||
#define LIBCORK_CORE_HASH_H |
|||
|
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/attributes.h> |
|||
#include <libcork/core/byte-order.h> |
|||
#include <libcork/core/types.h> |
|||
#include <libcork/core/u128.h> |
|||
|
|||
|
|||
#ifndef CORK_HASH_ATTRIBUTES |
|||
#define CORK_HASH_ATTRIBUTES CORK_ATTR_UNUSED static inline |
|||
#endif |
|||
|
|||
|
|||
typedef uint32_t cork_hash; |
|||
|
|||
typedef struct { |
|||
cork_u128 u128; |
|||
} cork_big_hash; |
|||
|
|||
#define cork_big_hash_equal(h1, h2) (cork_u128_eq((h1).u128, (h2).u128)) |
|||
|
|||
#define CORK_BIG_HASH_INIT() {{{{0}}}} |
|||
|
|||
/* We currently use MurmurHash3 [1], which is public domain, as our hash |
|||
* implementation. |
|||
* |
|||
* [1] http://code.google.com/p/smhasher/ |
|||
*/ |
|||
|
|||
#define CORK_ROTL32(a,b) (((a) << ((b) & 0x1f)) | ((a) >> (32 - ((b) & 0x1f)))) |
|||
#define CORK_ROTL64(a,b) (((a) << ((b) & 0x3f)) | ((a) >> (64 - ((b) & 0x3f)))) |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static inline |
|||
uint32_t cork_fmix32(uint32_t h) |
|||
{ |
|||
h ^= h >> 16; |
|||
h *= 0x85ebca6b; |
|||
h ^= h >> 13; |
|||
h *= 0xc2b2ae35; |
|||
h ^= h >> 16; |
|||
return h; |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static inline |
|||
uint64_t cork_fmix64(uint64_t k) |
|||
{ |
|||
k ^= k >> 33; |
|||
k *= UINT64_C(0xff51afd7ed558ccd); |
|||
k ^= k >> 33; |
|||
k *= UINT64_C(0xc4ceb9fe1a85ec53); |
|||
k ^= k >> 33; |
|||
return k; |
|||
} |
|||
|
|||
CORK_HASH_ATTRIBUTES |
|||
cork_hash |
|||
cork_stable_hash_buffer(cork_hash seed, const void *src, size_t len) |
|||
{ |
|||
typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; |
|||
|
|||
/* This is exactly the same as cork_murmur_hash_x86_32, but with a byte swap |
|||
* to make sure that we always process the uint32s little-endian. */ |
|||
const unsigned int nblocks = len / 4; |
|||
const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; |
|||
const cork_aliased_uint32_t *end = blocks + nblocks; |
|||
const cork_aliased_uint32_t *curr; |
|||
const uint8_t *tail = (const uint8_t *) end; |
|||
|
|||
uint32_t h1 = seed; |
|||
uint32_t c1 = 0xcc9e2d51; |
|||
uint32_t c2 = 0x1b873593; |
|||
uint32_t k1 = 0; |
|||
|
|||
/* body */ |
|||
for (curr = blocks; curr != end; curr++) { |
|||
uint32_t k1 = CORK_UINT32_HOST_TO_LITTLE(*curr); |
|||
|
|||
k1 *= c1; |
|||
k1 = CORK_ROTL32(k1,15); |
|||
k1 *= c2; |
|||
|
|||
h1 ^= k1; |
|||
h1 = CORK_ROTL32(h1,13); |
|||
h1 = h1*5+0xe6546b64; |
|||
} |
|||
|
|||
/* tail */ |
|||
switch (len & 3) { |
|||
case 3: k1 ^= tail[2] << 16; |
|||
case 2: k1 ^= tail[1] << 8; |
|||
case 1: k1 ^= tail[0]; |
|||
k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; |
|||
}; |
|||
|
|||
/* finalization */ |
|||
h1 ^= len; |
|||
h1 = cork_fmix32(h1); |
|||
return h1; |
|||
} |
|||
|
|||
#define cork_murmur_hash_x86_32(seed, src, len, dest) \ |
|||
do { \ |
|||
typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; \ |
|||
\ |
|||
const unsigned int nblocks = len / 4; \ |
|||
const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; \ |
|||
const cork_aliased_uint32_t *end = blocks + nblocks; \ |
|||
const cork_aliased_uint32_t *curr; \ |
|||
const uint8_t *tail = (const uint8_t *) end; \ |
|||
\ |
|||
uint32_t h1 = seed; \ |
|||
uint32_t c1 = 0xcc9e2d51; \ |
|||
uint32_t c2 = 0x1b873593; \ |
|||
uint32_t k1 = 0; \ |
|||
\ |
|||
/* body */ \ |
|||
for (curr = blocks; curr != end; curr++) { \ |
|||
uint32_t k1 = *curr; \ |
|||
\ |
|||
k1 *= c1; \ |
|||
k1 = CORK_ROTL32(k1,15); \ |
|||
k1 *= c2; \ |
|||
\ |
|||
h1 ^= k1; \ |
|||
h1 = CORK_ROTL32(h1,13); \ |
|||
h1 = h1*5+0xe6546b64; \ |
|||
} \ |
|||
\ |
|||
/* tail */ \ |
|||
switch (len & 3) { \ |
|||
case 3: k1 ^= tail[2] << 16; \ |
|||
case 2: k1 ^= tail[1] << 8; \ |
|||
case 1: k1 ^= tail[0]; \ |
|||
k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ |
|||
}; \ |
|||
\ |
|||
/* finalization */ \ |
|||
h1 ^= len; \ |
|||
h1 = cork_fmix32(h1); \ |
|||
*(dest) = h1; \ |
|||
} while (0) |
|||
|
|||
#define cork_murmur_hash_x86_128(seed, src, len, dest) \ |
|||
do { \ |
|||
typedef uint32_t __attribute__((__may_alias__)) cork_aliased_uint32_t; \ |
|||
\ |
|||
const unsigned int nblocks = len / 16; \ |
|||
const cork_aliased_uint32_t *blocks = (const cork_aliased_uint32_t *) src; \ |
|||
const cork_aliased_uint32_t *end = blocks + (nblocks * 4); \ |
|||
const cork_aliased_uint32_t *curr; \ |
|||
const uint8_t *tail = (const uint8_t *) end; \ |
|||
\ |
|||
uint32_t h1 = cork_u128_be32(seed.u128, 0); \ |
|||
uint32_t h2 = cork_u128_be32(seed.u128, 1); \ |
|||
uint32_t h3 = cork_u128_be32(seed.u128, 2); \ |
|||
uint32_t h4 = cork_u128_be32(seed.u128, 3); \ |
|||
\ |
|||
uint32_t c1 = 0x239b961b; \ |
|||
uint32_t c2 = 0xab0e9789; \ |
|||
uint32_t c3 = 0x38b34ae5; \ |
|||
uint32_t c4 = 0xa1e38b93; \ |
|||
\ |
|||
uint32_t k1 = 0; \ |
|||
uint32_t k2 = 0; \ |
|||
uint32_t k3 = 0; \ |
|||
uint32_t k4 = 0; \ |
|||
\ |
|||
/* body */ \ |
|||
for (curr = blocks; curr != end; curr += 4) { \ |
|||
uint32_t k1 = curr[0]; \ |
|||
uint32_t k2 = curr[1]; \ |
|||
uint32_t k3 = curr[2]; \ |
|||
uint32_t k4 = curr[3]; \ |
|||
\ |
|||
k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ |
|||
h1 = CORK_ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; \ |
|||
\ |
|||
k2 *= c2; k2 = CORK_ROTL32(k2,16); k2 *= c3; h2 ^= k2; \ |
|||
h2 = CORK_ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; \ |
|||
\ |
|||
k3 *= c3; k3 = CORK_ROTL32(k3,17); k3 *= c4; h3 ^= k3; \ |
|||
h3 = CORK_ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; \ |
|||
\ |
|||
k4 *= c4; k4 = CORK_ROTL32(k4,18); k4 *= c1; h4 ^= k4; \ |
|||
h4 = CORK_ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; \ |
|||
} \ |
|||
\ |
|||
/* tail */ \ |
|||
switch (len & 15) { \ |
|||
case 15: k4 ^= tail[14] << 16; \ |
|||
case 14: k4 ^= tail[13] << 8; \ |
|||
case 13: k4 ^= tail[12] << 0; \ |
|||
k4 *= c4; k4 = CORK_ROTL32(k4,18); k4 *= c1; h4 ^= k4; \ |
|||
\ |
|||
case 12: k3 ^= tail[11] << 24; \ |
|||
case 11: k3 ^= tail[10] << 16; \ |
|||
case 10: k3 ^= tail[ 9] << 8; \ |
|||
case 9: k3 ^= tail[ 8] << 0; \ |
|||
k3 *= c3; k3 = CORK_ROTL32(k3,17); k3 *= c4; h3 ^= k3; \ |
|||
\ |
|||
case 8: k2 ^= tail[ 7] << 24; \ |
|||
case 7: k2 ^= tail[ 6] << 16; \ |
|||
case 6: k2 ^= tail[ 5] << 8; \ |
|||
case 5: k2 ^= tail[ 4] << 0; \ |
|||
k2 *= c2; k2 = CORK_ROTL32(k2,16); k2 *= c3; h2 ^= k2; \ |
|||
\ |
|||
case 4: k1 ^= tail[ 3] << 24; \ |
|||
case 3: k1 ^= tail[ 2] << 16; \ |
|||
case 2: k1 ^= tail[ 1] << 8; \ |
|||
case 1: k1 ^= tail[ 0] << 0; \ |
|||
k1 *= c1; k1 = CORK_ROTL32(k1,15); k1 *= c2; h1 ^= k1; \ |
|||
}; \ |
|||
\ |
|||
/* finalization */ \ |
|||
\ |
|||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; \ |
|||
\ |
|||
h1 += h2; h1 += h3; h1 += h4; \ |
|||
h2 += h1; h3 += h1; h4 += h1; \ |
|||
\ |
|||
h1 = cork_fmix32(h1); \ |
|||
h2 = cork_fmix32(h2); \ |
|||
h3 = cork_fmix32(h3); \ |
|||
h4 = cork_fmix32(h4); \ |
|||
\ |
|||
h1 += h2; h1 += h3; h1 += h4; \ |
|||
h2 += h1; h3 += h1; h4 += h1; \ |
|||
\ |
|||
(dest)->u128 = cork_u128_from_32(h1, h2, h3, h4); \ |
|||
} while (0) |
|||
|
|||
#define cork_murmur_hash_x64_128(seed, src, len, dest) \ |
|||
do { \ |
|||
typedef uint64_t __attribute__((__may_alias__)) cork_aliased_uint64_t; \ |
|||
\ |
|||
const unsigned int nblocks = len / 16; \ |
|||
const cork_aliased_uint64_t *blocks = (const cork_aliased_uint64_t *) src; \ |
|||
const cork_aliased_uint64_t *end = blocks + (nblocks * 2); \ |
|||
const cork_aliased_uint64_t *curr; \ |
|||
const uint8_t *tail = (const uint8_t *) end; \ |
|||
\ |
|||
uint64_t h1 = cork_u128_be64(seed.u128, 0); \ |
|||
uint64_t h2 = cork_u128_be64(seed.u128, 1); \ |
|||
\ |
|||
uint64_t c1 = UINT64_C(0x87c37b91114253d5); \ |
|||
uint64_t c2 = UINT64_C(0x4cf5ad432745937f); \ |
|||
\ |
|||
uint64_t k1 = 0; \ |
|||
uint64_t k2 = 0; \ |
|||
\ |
|||
/* body */ \ |
|||
for (curr = blocks; curr != end; curr += 2) { \ |
|||
uint64_t k1 = curr[0]; \ |
|||
uint64_t k2 = curr[1]; \ |
|||
\ |
|||
k1 *= c1; k1 = CORK_ROTL64(k1,31); k1 *= c2; h1 ^= k1; \ |
|||
h1 = CORK_ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; \ |
|||
\ |
|||
k2 *= c2; k2 = CORK_ROTL64(k2,33); k2 *= c1; h2 ^= k2; \ |
|||
h2 = CORK_ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; \ |
|||
} \ |
|||
\ |
|||
/* tail */ \ |
|||
switch (len & 15) { \ |
|||
case 15: k2 ^= (uint64_t) (tail[14]) << 48; \ |
|||
case 14: k2 ^= (uint64_t) (tail[13]) << 40; \ |
|||
case 13: k2 ^= (uint64_t) (tail[12]) << 32; \ |
|||
case 12: k2 ^= (uint64_t) (tail[11]) << 24; \ |
|||
case 11: k2 ^= (uint64_t) (tail[10]) << 16; \ |
|||
case 10: k2 ^= (uint64_t) (tail[ 9]) << 8; \ |
|||
case 9: k2 ^= (uint64_t) (tail[ 8]) << 0; \ |
|||
k2 *= c2; k2 = CORK_ROTL64(k2,33); k2 *= c1; h2 ^= k2; \ |
|||
\ |
|||
case 8: k1 ^= (uint64_t) (tail[ 7]) << 56; \ |
|||
case 7: k1 ^= (uint64_t) (tail[ 6]) << 48; \ |
|||
case 6: k1 ^= (uint64_t) (tail[ 5]) << 40; \ |
|||
case 5: k1 ^= (uint64_t) (tail[ 4]) << 32; \ |
|||
case 4: k1 ^= (uint64_t) (tail[ 3]) << 24; \ |
|||
case 3: k1 ^= (uint64_t) (tail[ 2]) << 16; \ |
|||
case 2: k1 ^= (uint64_t) (tail[ 1]) << 8; \ |
|||
case 1: k1 ^= (uint64_t) (tail[ 0]) << 0; \ |
|||
k1 *= c1; k1 = CORK_ROTL64(k1,31); k1 *= c2; h1 ^= k1; \ |
|||
}; \ |
|||
\ |
|||
/* finalization */ \ |
|||
\ |
|||
h1 ^= len; h2 ^= len; \ |
|||
\ |
|||
h1 += h2; \ |
|||
h2 += h1; \ |
|||
\ |
|||
h1 = cork_fmix64(h1); \ |
|||
h2 = cork_fmix64(h2); \ |
|||
\ |
|||
h1 += h2; \ |
|||
h2 += h1; \ |
|||
\ |
|||
(dest)->u128 = cork_u128_from_64(h1, h2); \ |
|||
} while (0) |
|||
|
|||
|
|||
#include <stdio.h> |
|||
CORK_HASH_ATTRIBUTES |
|||
cork_hash |
|||
cork_hash_buffer(cork_hash seed, const void *src, size_t len) |
|||
{ |
|||
#if CORK_SIZEOF_POINTER == 8 |
|||
cork_big_hash big_seed = {cork_u128_from_32(seed, seed, seed, seed)}; |
|||
cork_big_hash hash; |
|||
cork_murmur_hash_x64_128(big_seed, src, len, &hash); |
|||
return cork_u128_be32(hash.u128, 0); |
|||
#else |
|||
cork_hash hash = 0; |
|||
cork_murmur_hash_x86_32(seed, src, len, &hash); |
|||
return hash; |
|||
#endif |
|||
} |
|||
|
|||
|
|||
CORK_HASH_ATTRIBUTES |
|||
cork_big_hash |
|||
cork_big_hash_buffer(cork_big_hash seed, const void *src, size_t len) |
|||
{ |
|||
cork_big_hash result; |
|||
#if CORK_SIZEOF_POINTER == 8 |
|||
cork_murmur_hash_x64_128(seed, src, len, &result); |
|||
#else |
|||
cork_murmur_hash_x86_128(seed, src, len, &result); |
|||
#endif |
|||
return result; |
|||
} |
|||
|
|||
|
|||
#define cork_hash_variable(seed, val) \ |
|||
(cork_hash_buffer((seed), &(val), sizeof((val)))) |
|||
#define cork_stable_hash_variable(seed, val) \ |
|||
(cork_stable_hash_buffer((seed), &(val), sizeof((val)))) |
|||
#define cork_big_hash_variable(seed, val) \ |
|||
(cork_big_hash_buffer((seed), &(val), sizeof((val)))) |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_HASH_H */ |
@ -0,0 +1,35 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_ID_H |
|||
#define LIBCORK_CORE_ID_H |
|||
|
|||
#include <libcork/core/hash.h> |
|||
|
|||
|
|||
struct cork_uid { |
|||
const char *name; |
|||
}; |
|||
|
|||
typedef const struct cork_uid *cork_uid; |
|||
|
|||
#define CORK_UID_NONE ((cork_uid) NULL) |
|||
|
|||
#define cork_uid_define_named(c_name, name) \ |
|||
static const struct cork_uid c_name##__id = { name }; \ |
|||
static cork_uid c_name = &c_name##__id; |
|||
#define cork_uid_define(c_name) \ |
|||
cork_uid_define_named(c_name, #c_name) |
|||
|
|||
#define cork_uid_equal(id1, id2) ((id1) == (id2)) |
|||
#define cork_uid_hash(id) ((cork_hash) (uintptr_t) (id)) |
|||
#define cork_uid_name(id) ((id)->name) |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_ID_H */ |
@ -0,0 +1,59 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2012-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORK_MEMPOOL_H |
|||
#define LIBCORK_CORK_MEMPOOL_H |
|||
|
|||
|
|||
#include <libcork/config.h> |
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/attributes.h> |
|||
#include <libcork/core/callbacks.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
#define CORK_MEMPOOL_DEFAULT_BLOCK_SIZE 4096 |
|||
|
|||
|
|||
struct cork_mempool; |
|||
|
|||
|
|||
CORK_API struct cork_mempool * |
|||
cork_mempool_new_size_ex(size_t element_size, size_t block_size); |
|||
|
|||
#define cork_mempool_new_size(element_size) \ |
|||
(cork_mempool_new_size_ex \ |
|||
((element_size), CORK_MEMPOOL_DEFAULT_BLOCK_SIZE)) |
|||
|
|||
#define cork_mempool_new_ex(type, block_size) \ |
|||
(cork_mempool_new_size_ex(sizeof(type), (block_size))) |
|||
|
|||
#define cork_mempool_new(type) \ |
|||
(cork_mempool_new_size(sizeof(type))) |
|||
|
|||
CORK_API void |
|||
cork_mempool_free(struct cork_mempool *mp); |
|||
|
|||
|
|||
CORK_API void |
|||
cork_mempool_set_callbacks(struct cork_mempool *mp, |
|||
void *user_data, cork_free_f free_user_data, |
|||
cork_init_f init_object, |
|||
cork_done_f done_object); |
|||
|
|||
|
|||
CORK_API void * |
|||
cork_mempool_new_object(struct cork_mempool *mp); |
|||
|
|||
|
|||
CORK_API void |
|||
cork_mempool_free_object(struct cork_mempool *mp, void *ptr); |
|||
|
|||
|
|||
#endif /* LIBCORK_CORK_MEMPOOL_H */ |
@ -0,0 +1,147 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011-2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_NET_ADDRESSES_H |
|||
#define LIBCORK_CORE_NET_ADDRESSES_H |
|||
|
|||
|
|||
#include <string.h> |
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/error.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
|
|||
/*----------------------------------------------------------------------- |
|||
* IP addresses |
|||
*/ |
|||
|
|||
struct cork_ipv4 { |
|||
union { |
|||
uint8_t u8[4]; |
|||
uint16_t u16[2]; |
|||
uint32_t u32; |
|||
} _; |
|||
}; |
|||
|
|||
struct cork_ipv6 { |
|||
union { |
|||
uint8_t u8[16]; |
|||
uint16_t u16[8]; |
|||
uint32_t u32[4]; |
|||
uint64_t u64[2]; |
|||
} _; |
|||
}; |
|||
|
|||
struct cork_ip { |
|||
/* Which version of IP address this is. */ |
|||
unsigned int version; |
|||
union { |
|||
struct cork_ipv4 v4; |
|||
struct cork_ipv6 v6; |
|||
} ip; |
|||
}; |
|||
|
|||
|
|||
#define CORK_IPV4_STRING_LENGTH (sizeof "xxx.xxx.xxx.xxx") |
|||
#define CORK_IPV6_STRING_LENGTH \ |
|||
(sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") |
|||
#define CORK_IP_STRING_LENGTH CORK_IPV6_STRING_LENGTH |
|||
|
|||
|
|||
/*** IPv4 ***/ |
|||
|
|||
/* src must be well-formed: 4 bytes, big-endian */ |
|||
#define cork_ipv4_copy(addr, src) \ |
|||
(memcpy((addr), (src), sizeof(struct cork_ipv4))) |
|||
|
|||
#define cork_ipv4_equal(a1, a2) \ |
|||
((a1)->_.u32 == (a2)->_.u32) |
|||
|
|||
CORK_API int |
|||
cork_ipv4_init(struct cork_ipv4 *addr, const char *str); |
|||
|
|||
CORK_API bool |
|||
cork_ipv4_equal_(const struct cork_ipv4 *addr1, const struct cork_ipv4 *addr2); |
|||
|
|||
CORK_API void |
|||
cork_ipv4_to_raw_string(const struct cork_ipv4 *addr, char *dest); |
|||
|
|||
CORK_API bool |
|||
cork_ipv4_is_valid_network(const struct cork_ipv4 *addr, |
|||
unsigned int cidr_prefix); |
|||
|
|||
|
|||
/*** IPv6 ***/ |
|||
|
|||
/* src must be well-formed: 16 bytes, big-endian */ |
|||
#define cork_ipv6_copy(addr, src) \ |
|||
(memcpy((addr), (src), sizeof(struct cork_ipv6))) |
|||
|
|||
#define cork_ipv6_equal(a1, a2) \ |
|||
((a1)->_.u64[0] == (a2)->_.u64[0] && \ |
|||
(a1)->_.u64[1] == (a2)->_.u64[1]) |
|||
|
|||
CORK_API int |
|||
cork_ipv6_init(struct cork_ipv6 *addr, const char *str); |
|||
|
|||
CORK_API bool |
|||
cork_ipv6_equal_(const struct cork_ipv6 *addr1, const struct cork_ipv6 *addr2); |
|||
|
|||
CORK_API void |
|||
cork_ipv6_to_raw_string(const struct cork_ipv6 *addr, char *dest); |
|||
|
|||
CORK_API bool |
|||
cork_ipv6_is_valid_network(const struct cork_ipv6 *addr, |
|||
unsigned int cidr_prefix); |
|||
|
|||
|
|||
/*** Generic IP ***/ |
|||
|
|||
#define cork_ip_equal(a1, a2) \ |
|||
((a1)->version == 4? \ |
|||
((a2)->version == 4 && cork_ipv4_equal(&(a1)->ip.v4, &(a2)->ip.v4)): \ |
|||
((a2)->version == 6 && cork_ipv6_equal(&(a1)->ip.v6, &(a2)->ip.v6))) |
|||
|
|||
/* src must be well-formed: 4 bytes, big-endian */ |
|||
#define cork_ip_from_ipv4(addr, src) \ |
|||
do { \ |
|||
(addr)->version = 4; \ |
|||
cork_ipv4_copy(&(addr)->ip.v4, (src)); \ |
|||
} while (0) |
|||
|
|||
/* src must be well-formed: 16 bytes, big-endian */ |
|||
#define cork_ip_from_ipv6(addr, src) \ |
|||
do { \ |
|||
(addr)->version = 6; \ |
|||
cork_ipv6_copy(&(addr)->ip.v6, (src)); \ |
|||
} while (0) |
|||
|
|||
/* src must be well-formed: 4 bytes, big-endian */ |
|||
CORK_API void |
|||
cork_ip_from_ipv4_(struct cork_ip *addr, const void *src); |
|||
|
|||
/* src must be well-formed: 16 bytes, big-endian */ |
|||
CORK_API void |
|||
cork_ip_from_ipv6_(struct cork_ip *addr, const void *src); |
|||
|
|||
CORK_API int |
|||
cork_ip_init(struct cork_ip *addr, const char *str); |
|||
|
|||
CORK_API bool |
|||
cork_ip_equal_(const struct cork_ip *addr1, const struct cork_ip *addr2); |
|||
|
|||
CORK_API void |
|||
cork_ip_to_raw_string(const struct cork_ip *addr, char *dest); |
|||
|
|||
CORK_API bool |
|||
cork_ip_is_valid_network(const struct cork_ip *addr, unsigned int cidr_prefix); |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_NET_ADDRESSES_H */ |
@ -0,0 +1,87 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_TIMESTAMP_H |
|||
#define LIBCORK_CORE_TIMESTAMP_H |
|||
|
|||
|
|||
#include <libcork/core/api.h> |
|||
#include <libcork/core/error.h> |
|||
#include <libcork/core/types.h> |
|||
#include <libcork/ds/buffer.h> |
|||
|
|||
|
|||
typedef uint64_t cork_timestamp; |
|||
|
|||
|
|||
#define cork_timestamp_init_sec(ts, sec) \ |
|||
do { \ |
|||
*(ts) = (((uint64_t) (sec)) << 32); \ |
|||
} while (0) |
|||
|
|||
#define cork_timestamp_init_gsec(ts, sec, gsec) \ |
|||
do { \ |
|||
*(ts) = (((uint64_t) (sec)) << 32) | \ |
|||
(((uint64_t) (gsec)) & 0xffffffff); \ |
|||
} while (0) |
|||
|
|||
#define cork_timestamp_init_msec(ts, sec, msec) \ |
|||
do { \ |
|||
*(ts) = (((uint64_t) (sec)) << 32) | \ |
|||
((((uint64_t) (msec)) << 32) / 1000); \ |
|||
} while (0) |
|||
|
|||
#define cork_timestamp_init_usec(ts, sec, usec) \ |
|||
do { \ |
|||
*(ts) = (((uint64_t) (sec)) << 32) | \ |
|||
((((uint64_t) (usec)) << 32) / 1000000); \ |
|||
} while (0) |
|||
|
|||
#define cork_timestamp_init_nsec(ts, sec, nsec) \ |
|||
do { \ |
|||
*(ts) = (((uint64_t) (sec)) << 32) | \ |
|||
((((uint64_t) (nsec)) << 32) / 1000000000); \ |
|||
} while (0) |
|||
|
|||
|
|||
CORK_API void |
|||
cork_timestamp_init_now(cork_timestamp *ts); |
|||
|
|||
|
|||
#define cork_timestamp_sec(ts) ((uint32_t) ((ts) >> 32)) |
|||
#define cork_timestamp_gsec(ts) ((uint32_t) ((ts) & 0xffffffff)) |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static inline uint64_t |
|||
cork_timestamp_gsec_to_units(const cork_timestamp ts, uint64_t denom) |
|||
{ |
|||
uint64_t half = ((uint64_t) 1 << 31) / denom; |
|||
uint64_t gsec = cork_timestamp_gsec(ts); |
|||
gsec += half; |
|||
gsec *= denom; |
|||
gsec >>= 32; |
|||
return gsec; |
|||
} |
|||
|
|||
#define cork_timestamp_msec(ts) cork_timestamp_gsec_to_units(ts, 1000) |
|||
#define cork_timestamp_usec(ts) cork_timestamp_gsec_to_units(ts, 1000000) |
|||
#define cork_timestamp_nsec(ts) cork_timestamp_gsec_to_units(ts, 1000000000) |
|||
|
|||
|
|||
CORK_API int |
|||
cork_timestamp_format_utc(const cork_timestamp ts, const char *format, |
|||
struct cork_buffer *dest); |
|||
|
|||
CORK_API int |
|||
cork_timestamp_format_local(const cork_timestamp ts, const char *format, |
|||
struct cork_buffer *dest); |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_TIMESTAMP_H */ |
@ -0,0 +1,82 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2011, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license |
|||
* details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_TYPES_H |
|||
#define LIBCORK_CORE_TYPES_H |
|||
|
|||
/* For now, we assume that the C99 integer types are available using the |
|||
* standard headers. */ |
|||
|
|||
#include <limits.h> |
|||
#include <inttypes.h> |
|||
#include <stdbool.h> |
|||
#include <stddef.h> |
|||
#include <stdint.h> |
|||
|
|||
|
|||
/* Define preprocessor macros that contain the size of several built-in |
|||
* types. Again, we assume that we have the C99 definitions available. */ |
|||
|
|||
#if SHRT_MAX == INT8_MAX |
|||
#define CORK_SIZEOF_SHORT 1 |
|||
#elif SHRT_MAX == INT16_MAX |
|||
#define CORK_SIZEOF_SHORT 2 |
|||
#elif SHRT_MAX == INT32_MAX |
|||
#define CORK_SIZEOF_SHORT 4 |
|||
#elif SHRT_MAX == INT64_MAX |
|||
#define CORK_SIZEOF_SHORT 8 |
|||
#else |
|||
#error "Cannot determine size of short" |
|||
#endif |
|||
|
|||
#if INT_MAX == INT8_MAX |
|||
#define CORK_SIZEOF_INT 1 |
|||
#elif INT_MAX == INT16_MAX |
|||
#define CORK_SIZEOF_INT 2 |
|||
#elif INT_MAX == INT32_MAX |
|||
#define CORK_SIZEOF_INT 4 |
|||
#elif INT_MAX == INT64_MAX |
|||
#define CORK_SIZEOF_INT 8 |
|||
#else |
|||
#error "Cannot determine size of int" |
|||
#endif |
|||
|
|||
#if LONG_MAX == INT8_MAX |
|||
#define CORK_SIZEOF_LONG 1 |
|||
#elif LONG_MAX == INT16_MAX |
|||
#define CORK_SIZEOF_LONG 2 |
|||
#elif LONG_MAX == INT32_MAX |
|||
#define CORK_SIZEOF_LONG 4 |
|||
#elif LONG_MAX == INT64_MAX |
|||
#define CORK_SIZEOF_LONG 8 |
|||
#else |
|||
#error "Cannot determine size of long" |
|||
#endif |
|||
|
|||
#if INTPTR_MAX == INT8_MAX |
|||
#define CORK_SIZEOF_POINTER 1 |
|||
#elif INTPTR_MAX == INT16_MAX |
|||
#define CORK_SIZEOF_POINTER 2 |
|||
#elif INTPTR_MAX == INT32_MAX |
|||
#define CORK_SIZEOF_POINTER 4 |
|||
#elif INTPTR_MAX == INT64_MAX |
|||
#define CORK_SIZEOF_POINTER 8 |
|||
#else |
|||
#error "Cannot determine size of void *" |
|||
#endif |
|||
|
|||
|
|||
/* Return a pointer to a @c struct, given a pointer to one of its |
|||
* fields. */ |
|||
#define cork_container_of(field, struct_type, field_name) \ |
|||
((struct_type *) (- offsetof(struct_type, field_name) + \ |
|||
(void *) (field))) |
|||
|
|||
#endif /* LIBCORK_CORE_TYPES_H */ |
@ -0,0 +1,223 @@ |
|||
/* -*- coding: utf-8 -*- |
|||
* ---------------------------------------------------------------------- |
|||
* Copyright © 2013, RedJack, LLC. |
|||
* All rights reserved. |
|||
* |
|||
* Please see the COPYING file in this distribution for license details. |
|||
* ---------------------------------------------------------------------- |
|||
*/ |
|||
|
|||
#ifndef LIBCORK_CORE_U128_H |
|||
#define LIBCORK_CORE_U128_H |
|||
|
|||
|
|||
#include <libcork/config.h> |
|||
#include <libcork/core/attributes.h> |
|||
#include <libcork/core/byte-order.h> |
|||
#include <libcork/core/types.h> |
|||
|
|||
typedef struct { |
|||
union { |
|||
uint8_t u8[16]; |
|||
uint16_t u16[8]; |
|||
uint32_t u32[4]; |
|||
uint64_t u64[2]; |
|||
#if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN |
|||
struct { uint64_t hi; uint64_t lo; } be64; |
|||
#else |
|||
struct { uint64_t lo; uint64_t hi; } be64; |
|||
#endif |
|||
#if CORK_CONFIG_HAVE_GCC_INT128 |
|||
#define CORK_U128_HAVE_U128 1 |
|||
unsigned __int128 u128; |
|||
#elif CORK_CONFIG_HAVE_GCC_MODE_ATTRIBUTE |
|||
#define CORK_U128_HAVE_U128 1 |
|||
unsigned int u128 __attribute__((mode(TI))); |
|||
#else |
|||
#define CORK_U128_HAVE_U128 0 |
|||
#endif |
|||
} _; |
|||
} cork_u128; |
|||
|
|||
|
|||
/* i0-3 are given in big-endian order, regardless of host endianness */ |
|||
CORK_ATTR_UNUSED |
|||
static cork_u128 |
|||
cork_u128_from_32(uint32_t i0, uint32_t i1, uint32_t i2, uint32_t i3) |
|||
{ |
|||
cork_u128 value; |
|||
#if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN |
|||
value._.u32[0] = i0; |
|||
value._.u32[1] = i1; |
|||
value._.u32[2] = i2; |
|||
value._.u32[3] = i3; |
|||
#else |
|||
value._.u32[3] = i0; |
|||
value._.u32[2] = i1; |
|||
value._.u32[1] = i2; |
|||
value._.u32[0] = i3; |
|||
#endif |
|||
return value; |
|||
} |
|||
|
|||
/* i0-1 are given in big-endian order, regardless of host endianness */ |
|||
CORK_ATTR_UNUSED |
|||
static cork_u128 |
|||
cork_u128_from_64(uint64_t i0, uint64_t i1) |
|||
{ |
|||
cork_u128 value; |
|||
#if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN |
|||
value._.u64[0] = i0; |
|||
value._.u64[1] = i1; |
|||
#else |
|||
value._.u64[1] = i0; |
|||
value._.u64[0] = i1; |
|||
#endif |
|||
return value; |
|||
} |
|||
|
|||
|
|||
#if CORK_HOST_ENDIANNESS == CORK_BIG_ENDIAN |
|||
#define cork_u128_be8(val, idx) ((val)._.u8[(idx)]) |
|||
#define cork_u128_be16(val, idx) ((val)._.u16[(idx)]) |
|||
#define cork_u128_be32(val, idx) ((val)._.u32[(idx)]) |
|||
#define cork_u128_be64(val, idx) ((val)._.u64[(idx)]) |
|||
#else |
|||
#define cork_u128_be8(val, idx) ((val)._.u8[15 - (idx)]) |
|||
#define cork_u128_be16(val, idx) ((val)._.u16[7 - (idx)]) |
|||
#define cork_u128_be32(val, idx) ((val)._.u32[3 - (idx)]) |
|||
#define cork_u128_be64(val, idx) ((val)._.u64[1 - (idx)]) |
|||
#endif |
|||
|
|||
|
|||
CORK_ATTR_UNUSED |
|||
static cork_u128 |
|||
cork_u128_add(cork_u128 a, cork_u128 b) |
|||
{ |
|||
cork_u128 result; |
|||
#if CORK_U128_HAVE_U128 |
|||
result._.u128 = a._.u128 + b._.u128; |
|||
#else |
|||
result._.be64.lo = a._.be64.lo + b._.be64.lo; |
|||
result._.be64.hi = |
|||
a._.be64.hi + b._.be64.hi + (result._.be64.lo < a._.be64.lo); |
|||
#endif |
|||
return result; |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static cork_u128 |
|||
cork_u128_sub(cork_u128 a, cork_u128 b) |
|||
{ |
|||
cork_u128 result; |
|||
#if CORK_U128_HAVE_U128 |
|||
result._.u128 = a._.u128 - b._.u128; |
|||
#else |
|||
result._.be64.lo = a._.be64.lo - b._.be64.lo; |
|||
result._.be64.hi = |
|||
a._.be64.hi - b._.be64.hi - (result._.be64.lo > a._.be64.lo); |
|||
#endif |
|||
return result; |
|||
} |
|||
|
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_eq(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 == b._.u128); |
|||
#else |
|||
return (a._.be64.hi == b._.be64.hi) && (a._.be64.lo == b._.be64.lo); |
|||
#endif |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_ne(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 != b._.u128); |
|||
#else |
|||
return (a._.be64.hi != b._.be64.hi) || (a._.be64.lo != b._.be64.lo); |
|||
#endif |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_lt(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 < b._.u128); |
|||
#else |
|||
if (a._.be64.hi == b._.be64.hi) { |
|||
return a._.be64.lo < b._.be64.lo; |
|||
} else { |
|||
return a._.be64.hi < b._.be64.hi; |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_le(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 <= b._.u128); |
|||
#else |
|||
if (a._.be64.hi == b._.be64.hi) { |
|||
return a._.be64.lo <= b._.be64.lo; |
|||
} else { |
|||
return a._.be64.hi <= b._.be64.hi; |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_gt(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 > b._.u128); |
|||
#else |
|||
if (a._.be64.hi == b._.be64.hi) { |
|||
return a._.be64.lo > b._.be64.lo; |
|||
} else { |
|||
return a._.be64.hi > b._.be64.hi; |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
CORK_ATTR_UNUSED |
|||
static bool |
|||
cork_u128_ge(cork_u128 a, cork_u128 b) |
|||
{ |
|||
#if CORK_U128_HAVE_U128 |
|||
return (a._.u128 >= b._.u128); |
|||
#else |
|||
if (a._.be64.hi == b._.be64.hi) { |
|||
return a._.be64.lo >= b._.be64.lo; |
|||
} else { |
|||
return a._.be64.hi >= b._.be64.hi; |
|||
} |
|||
#endif |
|||
} |
|||
|
|||
|
|||
/* log10(x) = log2(x) / log2(10) ~= log2(x) / 3.322 */ |
|||
#define CORK_U128_DECIMAL_LENGTH 44 /* ~= 128 / 3 + 1 + 1 */ |
|||
|
|||
CORK_API const char * |
|||
cork_u128_to_decimal(char *buf, cork_u128 val); |
|||
|
|||
|
|||
#define CORK_U128_HEX_LENGTH 33 |
|||
|
|||
CORK_API const char * |
|||
cork_u128_to_hex(char *buf, cork_u128 val); |
|||
|
|||
CORK_API const char * |
|||
cork_u128_to_padded_hex(char *buf, cork_u128 val); |
|||
|
|||
|
|||
#endif /* LIBCORK_CORE_U128_H */ |
Write
Preview
Loading…
Cancel
Save