Max Lv 11 years ago
parent
commit
173681171b
24 changed files with 3455 additions and 1 deletions
  1. 1
      .gitignore
  2. 0
      libcork/core/.dirstamp
  3. 131
      libcork/core/allocator.c
  4. 246
      libcork/core/error.c
  5. 407
      libcork/core/gc.c
  6. 20
      libcork/core/hash.c
  7. 529
      libcork/core/ip-address.c
  8. 179
      libcork/core/mempool.c
  9. 162
      libcork/core/timestamp.c
  10. 85
      libcork/core/u128.c
  11. 75
      libcork/include/libcork/core/allocator.h
  12. 25
      libcork/include/libcork/core/api.h
  13. 172
      libcork/include/libcork/core/attributes.h
  14. 186
      libcork/include/libcork/core/byte-order.h
  15. 43
      libcork/include/libcork/core/callbacks.h
  16. 139
      libcork/include/libcork/core/error.h
  17. 67
      libcork/include/libcork/core/gc.h
  18. 356
      libcork/include/libcork/core/hash.h
  19. 35
      libcork/include/libcork/core/id.h
  20. 59
      libcork/include/libcork/core/mempool.h
  21. 147
      libcork/include/libcork/core/net-addresses.h
  22. 87
      libcork/include/libcork/core/timestamp.h
  23. 82
      libcork/include/libcork/core/types.h
  24. 223
      libcork/include/libcork/core/u128.h

1
.gitignore

@ -30,7 +30,6 @@ debian/*.debhelper*
*~
*.bak
*.bin
core
*.dll
*.exe
*-ISO*.bdf

0
libcork/core/.dirstamp

131
libcork/core/allocator.c

@ -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;
}

246
libcork/core/error.c

@ -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);
}

407
libcork/core/gc.c

@ -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);
}

20
libcork/core/hash.c

@ -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
*/

529
libcork/core/ip-address.c

@ -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;
}
}

179
libcork/core/mempool.c

@ -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--;
}

162
libcork/core/timestamp.c

@ -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);
}

85
libcork/core/u128.c

@ -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;
}

75
libcork/include/libcork/core/allocator.h

@ -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 */

25
libcork/include/libcork/core/api.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 */

172
libcork/include/libcork/core/attributes.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 */

186
libcork/include/libcork/core/byte-order.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 */

43
libcork/include/libcork/core/callbacks.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 */

139
libcork/include/libcork/core/error.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 */

67
libcork/include/libcork/core/gc.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 */

356
libcork/include/libcork/core/hash.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 */

35
libcork/include/libcork/core/id.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 */

59
libcork/include/libcork/core/mempool.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 */

147
libcork/include/libcork/core/net-addresses.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 */

87
libcork/include/libcork/core/timestamp.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 */

82
libcork/include/libcork/core/types.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 */

223
libcork/include/libcork/core/u128.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 */
Loading…
Cancel
Save