summaryrefslogtreecommitdiff
path: root/src/libaudcore
diff options
context:
space:
mode:
authorAlessio Treglia <alessio@debian.org>2014-06-12 12:30:32 +0100
committerAlessio Treglia <alessio@debian.org>2014-06-12 12:30:32 +0100
commit260690bc6b904d1ca0025e539b9663ddb9fd2424 (patch)
tree6638a0254217dc8443bdd4ff00a5d78c5a3da556 /src/libaudcore
parentb380f841f764543f477b658af25194c4d9f8a1eb (diff)
Imported Upstream version 3.5
Diffstat (limited to 'src/libaudcore')
-rw-r--r--src/libaudcore/Makefile17
-rw-r--r--src/libaudcore/audio.c7
-rw-r--r--src/libaudcore/audio.h.in2
-rw-r--r--src/libaudcore/audstrings.c599
-rw-r--r--src/libaudcore/audstrings.h101
-rw-r--r--src/libaudcore/charset.c180
-rw-r--r--src/libaudcore/core.h27
-rw-r--r--src/libaudcore/eventqueue.c31
-rw-r--r--src/libaudcore/hook.c2
-rw-r--r--src/libaudcore/hook.h3
-rw-r--r--src/libaudcore/index.c142
-rw-r--r--src/libaudcore/index.h66
-rw-r--r--src/libaudcore/inifile.c134
-rw-r--r--src/libaudcore/inifile.h33
-rw-r--r--src/libaudcore/multihash.c153
-rw-r--r--src/libaudcore/multihash.h95
-rw-r--r--src/libaudcore/strpool.c259
-rw-r--r--src/libaudcore/tinylock.c65
-rw-r--r--src/libaudcore/tinylock.h.in56
-rw-r--r--src/libaudcore/tuple.c286
-rw-r--r--src/libaudcore/tuple.h49
-rw-r--r--src/libaudcore/tuple_compiler.c182
-rw-r--r--src/libaudcore/tuple_compiler.h5
-rw-r--r--src/libaudcore/tuple_formatter.c74
-rw-r--r--src/libaudcore/vfs.c103
-rw-r--r--src/libaudcore/vfs.h34
-rw-r--r--src/libaudcore/vfs_async.c2
-rw-r--r--src/libaudcore/vfs_common.c280
-rw-r--r--src/libaudcore/vfs_local.c231
-rw-r--r--src/libaudcore/vfs_local.h (renamed from src/libaudcore/tuple_formatter.h)15
30 files changed, 2144 insertions, 1089 deletions
diff --git a/src/libaudcore/Makefile b/src/libaudcore/Makefile
index c823ae7..8f380f7 100644
--- a/src/libaudcore/Makefile
+++ b/src/libaudcore/Makefile
@@ -1,25 +1,33 @@
SHARED_LIB = ${LIB_PREFIX}audcore${LIB_SUFFIX}
-LIB_MAJOR = 1
+LIB_MAJOR = 2
LIB_MINOR = 0
SRCS = audio.c \
audstrings.c \
+ charset.c \
eventqueue.c \
hook.c \
index.c \
+ inifile.c \
+ multihash.c \
strpool.c \
+ tinylock.c \
tuple.c \
tuple_compiler.c \
tuple_formatter.c \
vfs.c \
vfs_async.c \
- vfs_common.c
+ vfs_common.c \
+ vfs_local.c
INCLUDES = audio.h \
audstrings.h \
core.h \
hook.h \
index.h \
+ inifile.h \
+ multihash.h \
+ tinylock.h \
tuple.h \
vfs.h \
vfs_async.h
@@ -32,7 +40,10 @@ includesubdir = libaudcore
CPPFLAGS := -I.. -I../.. \
${CPPFLAGS} \
${GLIB_CFLAGS} \
+ ${LIBGUESS_CFLAGS}
CFLAGS += ${LIB_CFLAGS}
-LIBS += ${GLIB_LIBS} -lm
+LIBS += -lm \
+ ${GLIB_LIBS} \
+ ${LIBGUESS_LIBS}
diff --git a/src/libaudcore/audio.c b/src/libaudcore/audio.c
index f866509..25246be 100644
--- a/src/libaudcore/audio.c
+++ b/src/libaudcore/audio.c
@@ -1,6 +1,6 @@
/*
* audio.c
- * Copyright 2009-2012 John Lindgren, Michał Lipski, and Anders Johansson
+ * Copyright 2009-2013 John Lindgren, Michał Lipski, and Anders Johansson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,6 +22,7 @@
#include <math.h>
#include "audio.h"
+#include "core.h"
#define INTERLACE_LOOP(TYPE) \
for (int c = 0; c < channels; c ++) \
@@ -173,7 +174,7 @@ EXPORT void audio_from_int (const void * in, int format, float * out, int sample
{
int entry;
- for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++)
+ for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++)
{
if (convert_table[entry].format == format)
{
@@ -187,7 +188,7 @@ EXPORT void audio_to_int (const float * in, void * out, int format, int samples)
{
int entry;
- for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++)
+ for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++)
{
if (convert_table[entry].format == format)
{
diff --git a/src/libaudcore/audio.h.in b/src/libaudcore/audio.h.in
index 19cfef2..2e9dd5a 100644
--- a/src/libaudcore/audio.h.in
+++ b/src/libaudcore/audio.h.in
@@ -1,6 +1,6 @@
/*
* audio.h
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/audstrings.c b/src/libaudcore/audstrings.c
index eb4b8f1..c1d878b 100644
--- a/src/libaudcore/audstrings.c
+++ b/src/libaudcore/audstrings.c
@@ -1,7 +1,6 @@
/*
* audstrings.c
- * Copyright 2009-2011 John Lindgren
- * Copyright 2010 William Pitcock
+ * Copyright 2009-2012 John Lindgren and William Pitcock
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -22,19 +21,67 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
-#include <glib.h>
#include <string.h>
-#include <ctype.h>
-#include <locale.h>
+
+#include <glib.h>
#include <audacious/i18n.h>
#include "audstrings.h"
+#include "index.h"
+
+static const char ascii_to_hex[256] = {
+ ['0'] = 0x0, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4,
+ ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7, ['8'] = 0x8, ['9'] = 0x9,
+ ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf,
+ ['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd, ['E'] = 0xe, ['F'] = 0xf
+};
+
+static const char hex_to_ascii[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static const char uri_legal_table[256] = {
+ ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1,
+ ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1,
+ ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1,
+ ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1,
+ ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1, ['u'] = 1,
+ ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1,
+ ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1,
+ ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1,
+ ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1,
+ ['V'] = 1, ['W'] = 1, ['X'] = 1, ['Y'] = 1, ['Z'] = 1,
+ ['-'] = 1, ['_'] = 1, ['.'] = 1, ['~'] = 1, ['/'] = 1
+};
+
+static const char swap_case[256] =
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
+ "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ\0\0\0\0\0";
+
+#define FROM_HEX(c) (ascii_to_hex[(unsigned char) (c)])
+#define TO_HEX(i) (hex_to_ascii[(i) & 15])
+#define IS_LEGAL(c) (uri_legal_table[(unsigned char) (c)])
+#define SWAP_CASE(c) (swap_case[(unsigned char) (c)])
+
+EXPORT char * str_printf (const char * format, ...)
+{
+ va_list args;
+ va_start (args, format);
+
+ char * str = str_vprintf (format, args);
-#define FROM_HEX(c) ((c) < 'A' ? (c) - '0' : (c) < 'a' ? 10 + (c) - 'A' : 10 + (c) - 'a')
-#define TO_HEX(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
-#define IS_LEGAL(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') \
- || ((c) >= '0' && (c) <= '9') || (strchr ("-_.~/", (c))))
+ va_end (args);
+ return str;
+}
+
+EXPORT char * str_vprintf (const char * format, va_list args)
+{
+ VSPRINTF (buf, format, args);
+ return str_get (buf);
+}
EXPORT bool_t str_has_prefix_nocase (const char * str, const char * prefix)
{
@@ -52,60 +99,154 @@ EXPORT bool_t str_has_suffix_nocase (const char * str, const char * suffix)
return ! g_ascii_strcasecmp (str + len1 - len2, suffix);
}
-static char * (* str_to_utf8_impl) (const char *) = NULL;
-static char * (* str_to_utf8_full_impl) (const char *, int, int *, int *) = NULL;
-
-EXPORT void str_set_utf8_impl (char * (* stu_impl) (const char *),
- char * (* stuf_impl) (const char *, int, int *, int *))
+EXPORT char * strstr_nocase (const char * haystack, const char * needle)
{
- str_to_utf8_impl = stu_impl;
- str_to_utf8_full_impl = stuf_impl;
+ while (1)
+ {
+ const char * ap = haystack;
+ const char * bp = needle;
+
+ while (1)
+ {
+ char a = * ap ++;
+ char b = * bp ++;
+
+ if (! b) /* all of needle matched */
+ return (char *) haystack;
+ if (! a) /* end of haystack reached */
+ return NULL;
+
+ if (a != b && a != SWAP_CASE (b))
+ break;
+ }
+
+ haystack ++;
+ }
}
-EXPORT char * str_to_utf8 (const char * str)
+EXPORT char * strstr_nocase_utf8 (const char * haystack, const char * needle)
{
- g_return_val_if_fail (str_to_utf8_impl, NULL);
- return str_to_utf8_impl (str);
+ while (1)
+ {
+ const char * ap = haystack;
+ const char * bp = needle;
+
+ while (1)
+ {
+ gunichar a = g_utf8_get_char (ap);
+ gunichar b = g_utf8_get_char (bp);
+
+ if (! b) /* all of needle matched */
+ return (char *) haystack;
+ if (! a) /* end of haystack reached */
+ return NULL;
+
+ if (a != b && (a < 128 ? SWAP_CASE (a) != b :
+ g_unichar_tolower (a) != g_unichar_tolower (b)))
+ break;
+
+ ap = g_utf8_next_char (ap);
+ bp = g_utf8_next_char (bp);
+ }
+
+ haystack = g_utf8_next_char (haystack);
+ }
}
-EXPORT char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written)
+EXPORT char * str_tolower_utf8 (const char * str)
{
- g_return_val_if_fail (str_to_utf8_full_impl, NULL);
- return str_to_utf8_full_impl (str, len, bytes_read, bytes_written);
+ char buf[6 * strlen (str) + 1];
+ const char * get = str;
+ char * set = buf;
+ gunichar c;
+
+ while ((c = g_utf8_get_char (get)))
+ {
+ if (c < 128)
+ * set ++ = g_ascii_tolower (c);
+ else
+ set += g_unichar_to_utf8 (g_unichar_tolower (c), set);
+
+ get = g_utf8_next_char (get);
+ }
+
+ * set = 0;
+
+ return str_get (buf);
}
-EXPORT void string_replace_char (char * string, char old_c, char new_c)
+EXPORT void str_replace_char (char * string, char old_c, char new_c)
{
while ((string = strchr (string, old_c)))
* string ++ = new_c;
}
+EXPORT void str_itoa (int x, char * buf, int bufsize)
+{
+ if (! bufsize)
+ return;
+
+ if (x < 0)
+ {
+ if (bufsize > 1)
+ {
+ * buf ++ = '-';
+ bufsize --;
+ }
+
+ x = -x;
+ }
+
+ char * rev = buf + bufsize - 1;
+ * rev = 0;
+
+ while (rev > buf)
+ {
+ * (-- rev) = '0' + x % 10;
+ if (! (x /= 10))
+ break;
+ }
+
+ while ((* buf ++ = * rev ++));
+}
+
/* Percent-decodes up to <len> bytes of <str> to <out>, which must be large
* enough to hold the decoded string (i.e., (len + 1) bytes). If <len> is
* negative, decodes all of <str>. */
EXPORT void str_decode_percent (const char * str, int len, char * out)
{
+ const char * nul;
+
if (len < 0)
- len = INT_MAX;
+ len = strlen (str);
+ else if ((nul = memchr (str, 0, len)))
+ len = nul - str;
- while (len --)
+ while (1)
{
- char c = * str ++;
- if (! c)
+ const char * p = memchr (str, '%', len);
+ if (! p)
break;
- if (c == '%' && len >= 2 && str[0] && str[1])
- {
- c = (FROM_HEX (str[0]) << 4) | FROM_HEX (str[1]);
- str += 2;
- len -= 2;
- }
+ int block = p - str;
+ memmove (out, str, block);
+
+ str += block;
+ out += block;
+ len -= block;
+
+ if (len < 3)
+ break;
+
+ * out ++ = (FROM_HEX (str[1]) << 4) | FROM_HEX (str[2]);
- * out ++ = c;
+ str += 3;
+ len -= 3;
}
- * out = 0;
+ memmove (out, str, len);
+ out[len] = 0;
}
/* Percent-encodes up to <len> bytes of <str> to <out>, which must be large
@@ -114,14 +255,16 @@ EXPORT void str_decode_percent (const char * str, int len, char * out)
EXPORT void str_encode_percent (const char * str, int len, char * out)
{
+ const char * nul;
+
if (len < 0)
- len = INT_MAX;
+ len = strlen (str);
+ else if ((nul = memchr (str, 0, len)))
+ len = nul - str;
while (len --)
{
char c = * str ++;
- if (! c)
- break;
if (IS_LEGAL (c))
* out ++ = c;
@@ -136,62 +279,101 @@ EXPORT void str_encode_percent (const char * str, int len, char * out)
* out = 0;
}
-/* Like g_filename_to_uri, but converts the filename from the system locale to
- * UTF-8 before percent-encoding. On Windows, replaces '\' with '/' and adds a
- * leading '/'. */
+EXPORT void filename_normalize (char * filename)
+{
+#ifdef _WIN32
+ /* convert slash to backslash on Windows */
+ str_replace_char (filename, '/', '\\');
+#endif
-EXPORT char * filename_to_uri (const char * name)
+ /* remove trailing slash */
+ int len = strlen (filename);
+#ifdef _WIN32
+ if (len > 3 && filename[len - 1] == '\\') /* leave "C:\" */
+#else
+ if (len > 1 && filename[len - 1] == '/') /* leave leading "/" */
+#endif
+ filename[len - 1] = 0;
+}
+
+EXPORT char * filename_build (const char * path, const char * name)
{
- char * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
- if (! utf8)
+ int len = strlen (path);
+
+#ifdef _WIN32
+ if (! len || path[len - 1] == '/' || path[len - 1] == '\\')
{
- const char * locale = setlocale (LC_ALL, NULL);
- fprintf (stderr, "Cannot convert filename from system locale (%s): %s\n", locale, name);
- return NULL;
+ SCONCAT2 (filename, path, name);
+ return str_get (filename);
+ }
+
+ SCONCAT3 (filename, path, "\\", name);
+ return str_get (filename);
+#else
+ if (! len || path[len - 1] == '/')
+ {
+ SCONCAT2 (filename, path, name);
+ return str_get (filename);
}
+ SCONCAT3 (filename, path, "/", name);
+ return str_get (filename);
+#endif
+}
+
#ifdef _WIN32
- string_replace_char (utf8, '\\', '/');
+#define URI_PREFIX "file:///"
+#define URI_PREFIX_LEN 8
+#else
+#define URI_PREFIX "file://"
+#define URI_PREFIX_LEN 7
#endif
- char enc[3 * strlen (utf8) + 1];
- str_encode_percent (utf8, -1, enc);
- g_free (utf8);
+/* Like g_filename_to_uri, but converts the filename from the system locale to
+ * UTF-8 before percent-encoding (except on Windows, where filenames are assumed
+ * to be UTF-8). On Windows, replaces '\' with '/' and adds a leading '/'. */
+EXPORT char * filename_to_uri (const char * name)
+{
#ifdef _WIN32
- return g_strdup_printf ("file:///%s", enc);
+ SCOPY (utf8, name);
+ str_replace_char (utf8, '\\', '/');
#else
- return g_strdup_printf ("file://%s", enc);
+ char * utf8 = str_from_locale (name, -1);
+ if (! utf8)
+ return NULL;
#endif
+
+ char enc[URI_PREFIX_LEN + 3 * strlen (utf8) + 1];
+ strcpy (enc, URI_PREFIX);
+ str_encode_percent (utf8, -1, enc + URI_PREFIX_LEN);
+
+#ifndef _WIN32
+ str_unref (utf8);
+#endif
+
+ return str_get (enc);
}
/* Like g_filename_from_uri, but converts the filename from UTF-8 to the system
- * locale after percent-decoding. On Windows, strips the leading '/' and
- * replaces '/' with '\'. */
+ * locale after percent-decoding (except on Windows, where filenames are assumed
+ * to be UTF-8). On Windows, strips the leading '/' and replaces '/' with '\'. */
EXPORT char * uri_to_filename (const char * uri)
{
+ if (strncmp (uri, URI_PREFIX, URI_PREFIX_LEN))
+ return NULL;
+
+ char buf[strlen (uri + URI_PREFIX_LEN) + 1];
+ str_decode_percent (uri + URI_PREFIX_LEN, -1, buf);
+
+ filename_normalize (buf);
+
#ifdef _WIN32
- g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL);
- char buf[strlen (uri + 8) + 1];
- str_decode_percent (uri + 8, -1, buf);
+ return str_get (buf);
#else
- g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL);
- char buf[strlen (uri + 7) + 1];
- str_decode_percent (uri + 7, -1, buf);
-#endif
-#ifdef _WIN32
- string_replace_char (buf, '/', '\\');
+ return str_to_locale (buf, -1);
#endif
-
- char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL);
- if (! name)
- {
- const char * locale = setlocale (LC_ALL, NULL);
- fprintf (stderr, "Cannot convert filename to system locale (%s): %s\n", locale, buf);
- }
-
- return name;
}
/* Formats a URI for human-readable display. Percent-decodes and, for file://
@@ -200,26 +382,26 @@ EXPORT char * uri_to_filename (const char * uri)
EXPORT char * uri_to_display (const char * uri)
{
if (! strncmp (uri, "cdda://?", 8))
- return g_strdup_printf (_("Audio CD, track %s"), uri + 8);
+ return str_printf (_("Audio CD, track %s"), uri + 8);
char buf[strlen (uri) + 1];
-#ifdef _WIN32
- if (! strncmp (uri, "file:///", 8))
+ if (! strncmp (uri, URI_PREFIX, URI_PREFIX_LEN))
{
- str_decode_percent (uri + 8, -1, buf);
- string_replace_char (buf, '/', '\\');
- }
-#else
- if (! strncmp (uri, "file://", 7))
- str_decode_percent (uri + 7, -1, buf);
+ str_decode_percent (uri + URI_PREFIX_LEN, -1, buf);
+#ifdef _WIN32
+ str_replace_char (buf, '/', '\\');
#endif
+ }
else
str_decode_percent (uri, -1, buf);
- return g_strdup (buf);
+ return str_get (buf);
}
+#undef URI_PREFIX
+#undef URI_PREFIX_LEN
+
EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
const char * * sub_p, int * isub_p)
{
@@ -238,9 +420,7 @@ EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * e
else
sub = end;
- char buf[sub - base + 1];
- memcpy (buf, base, sub - base);
- buf[sub - base] = 0;
+ SNCOPY (buf, base, sub - base);
if ((c = strrchr (buf, '.')))
ext = base + (c - buf);
@@ -265,7 +445,8 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
if (ext[0] != '.')
return FALSE;
- g_strlcpy (buf, ext + 1, buflen);
+ strncpy (buf, ext + 1, buflen - 1);
+ buf[buflen - 1] = 0;
/* remove subtunes and HTTP query strings */
char * qmark;
@@ -279,7 +460,7 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen)
/* Non-ASCII characters are treated exactly as is. */
/* Handles NULL gracefully. */
-EXPORT int string_compare (const char * ap, const char * bp)
+EXPORT int str_compare (const char * ap, const char * bp)
{
if (ap == NULL)
return (bp == NULL) ? 0 : -1;
@@ -321,9 +502,9 @@ EXPORT int string_compare (const char * ap, const char * bp)
return 0;
}
-/* Decodes percent-encoded strings, then compares then with string_compare. */
+/* Decodes percent-encoded strings, then compares then with str_compare. */
-EXPORT int string_compare_encoded (const char * ap, const char * bp)
+EXPORT int str_compare_encoded (const char * ap, const char * bp)
{
if (ap == NULL)
return (bp == NULL) ? 0 : -1;
@@ -376,37 +557,72 @@ EXPORT int string_compare_encoded (const char * ap, const char * bp)
return 0;
}
-EXPORT char *
-str_replace_fragment(char *s, int size, const char *old, const char *new)
+EXPORT Index * str_list_to_index (const char * list, const char * delims)
{
- char *ptr = s;
- int left = strlen(s);
- int avail = size - (left + 1);
- int oldlen = strlen(old);
- int newlen = strlen(new);
- int diff = newlen - oldlen;
-
- while (left >= oldlen)
+ char dmap[256] = {0};
+
+ for (; * delims; delims ++)
+ dmap[(unsigned char) (* delims)] = 1;
+
+ Index * index = index_new ();
+ const char * word = NULL;
+
+ for (; * list; list ++)
{
- if (strncmp(ptr, old, oldlen))
+ if (dmap[(unsigned char) (* list)])
{
- left--;
- ptr++;
- continue;
+ if (word)
+ {
+ index_insert (index, -1, str_nget (word, list - word));
+ word = NULL;
+ }
}
+ else
+ {
+ if (! word)
+ {
+ word = list;
+ }
+ }
+ }
- if (diff > avail)
- break;
+ if (word)
+ index_insert (index, -1, str_get (word));
- if (diff != 0)
- memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen);
+ return index;
+}
+
+EXPORT char * index_to_str_list (Index * index, const char * sep)
+{
+ int count = index_count (index);
+ int seplen = strlen (sep);
+ int total = count ? seplen * (count - 1) : 0;
+ int lengths[count];
- memcpy(ptr, new, newlen);
- ptr += newlen;
- left -= oldlen;
+ for (int i = 0; i < count; i ++)
+ {
+ lengths[i] = strlen (index_get (index, i));
+ total += lengths[i];
}
- return s;
+ char buf[total + 1];
+ int pos = 0;
+
+ for (int i = 0; i < count; i ++)
+ {
+ if (i)
+ {
+ strcpy (buf + pos, sep);
+ pos += seplen;
+ }
+
+ strcpy (buf + pos, index_get (index, i));
+ pos += lengths[i];
+ }
+
+ buf[pos] = 0;
+
+ return str_get (buf);
}
/*
@@ -420,11 +636,11 @@ str_replace_fragment(char *s, int size, const char *old, const char *new)
* architecture or locale we have to deal with.
* - Readability, meaning that the number one is rendered "1", not "1.000".
*
- * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and
+ * Values between -1,000,000,000 and 1,000,000,000 (inclusive) are guaranteed to
* have an accuracy of 6 decimal places.
*/
-EXPORT bool_t string_to_int (const char * string, int * addr)
+EXPORT int str_to_int (const char * string)
{
bool_t neg = (string[0] == '-');
if (neg)
@@ -433,88 +649,41 @@ EXPORT bool_t string_to_int (const char * string, int * addr)
int val = 0;
char c;
- while ((c = * string ++))
- {
- if (c < '0' || c > '9' || val > 100000000)
- goto ERR;
-
+ while ((c = * string ++) && c >= '0' && c <= '9')
val = val * 10 + (c - '0');
- }
-
- if (val > 1000000000)
- goto ERR;
- * addr = neg ? -val : val;
- return TRUE;
-
-ERR:
- return FALSE;
+ return neg ? -val : val;
}
-EXPORT bool_t string_to_double (const char * string, double * addr)
+EXPORT double str_to_double (const char * string)
{
bool_t neg = (string[0] == '-');
if (neg)
string ++;
+ double val = str_to_int (string);
const char * p = strchr (string, '.');
- int i, f;
if (p)
{
- char buf[11];
- int len;
-
- len = p - string;
- if (len > 10)
- goto ERR;
-
- memcpy (buf, string, len);
- buf[len] = 0;
-
- if (! string_to_int (buf, & i))
- goto ERR;
-
- len = strlen (p + 1);
- if (len > 6)
- goto ERR;
-
- memcpy (buf, p + 1, len);
- memset (buf + len, '0', 6 - len);
- buf[6] = 0;
-
- if (! string_to_int (buf, & f))
- goto ERR;
+ char buf[7] = "000000";
+ const char * nul = memchr (p + 1, 0, 6);
+ memcpy (buf, p + 1, nul ? nul - (p + 1) : 6);
+ val += (double) str_to_int (buf) / 1000000;
}
- else
- {
- if (! string_to_int (string, & i))
- goto ERR;
-
- f = 0;
- }
-
- double val = i + (double) f / 1000000;
- if (val > 1000000000)
- goto ERR;
-
- * addr = neg ? -val : val;
- return TRUE;
-ERR:
- return FALSE;
+ return neg ? -val : val;
}
-EXPORT char * int_to_string (int val)
+EXPORT char * int_to_str (int val)
{
- g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
- return g_strdup_printf ("%d", val);
+ char buf[16];
+ str_itoa (val, buf, sizeof buf);
+ return str_get (buf);
}
-EXPORT char * double_to_string (double val)
+EXPORT char * double_to_str (double val)
{
- g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL);
-
bool_t neg = (val < 0);
if (neg)
val = -val;
@@ -528,94 +697,88 @@ EXPORT char * double_to_string (double val)
f = 0;
}
- char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f);
+ SPRINTF (buf, "%s%d.%06d", neg ? "-" : "", i, f);
- char * c = s + strlen (s);
+ char * c = buf + strlen (buf);
while (* (c - 1) == '0')
c --;
if (* (c - 1) == '.')
c --;
* c = 0;
- return s;
+ return str_get (buf);
}
-EXPORT bool_t string_to_int_array (const char * string, int * array, int count)
+EXPORT bool_t str_to_int_array (const char * string, int * array, int count)
{
- char * * split = g_strsplit (string, ",", -1);
- if (g_strv_length (split) != count)
- goto ERR;
+ Index * index = str_list_to_index (string, ", ");
+ bool_t okay = (index_count (index) == count);
- for (int i = 0; i < count; i ++)
+ if (okay)
{
- if (! string_to_int (split[i], & array[i]))
- goto ERR;
+ for (int i = 0; i < count; i ++)
+ array[i] = str_to_int (index_get (index, i));
}
- g_strfreev (split);
- return TRUE;
-
-ERR:
- g_strfreev (split);
- return FALSE;
+ index_free_full (index, (IndexFreeFunc) str_unref);
+ return okay;
}
-EXPORT char * int_array_to_string (const int * array, int count)
+EXPORT char * int_array_to_str (const int * array, int count)
{
- char * * split = g_malloc0 (sizeof (char *) * (count + 1));
+ Index * index = index_new ();
for (int i = 0; i < count; i ++)
{
- split[i] = int_to_string (array[i]);
- if (! split[i])
+ char * value = int_to_str (array[i]);
+ if (! value)
goto ERR;
+
+ index_insert (index, -1, value);
}
- char * string = g_strjoinv (",", split);
- g_strfreev (split);
+ char * string = index_to_str_list (index, ",");
+ index_free_full (index, (IndexFreeFunc) str_unref);
return string;
ERR:
- g_strfreev (split);
+ index_free_full (index, (IndexFreeFunc) str_unref);
return NULL;
}
-EXPORT bool_t string_to_double_array (const char * string, double * array, int count)
+EXPORT bool_t str_to_double_array (const char * string, double * array, int count)
{
- char * * split = g_strsplit (string, ",", -1);
- if (g_strv_length (split) != count)
- goto ERR;
+ Index * index = str_list_to_index (string, ", ");
+ bool_t okay = (index_count (index) == count);
- for (int i = 0; i < count; i ++)
+ if (okay)
{
- if (! string_to_double (split[i], & array[i]))
- goto ERR;
+ for (int i = 0; i < count; i ++)
+ array[i] = str_to_double (index_get (index, i));
}
- g_strfreev (split);
- return TRUE;
-
-ERR:
- g_strfreev (split);
- return FALSE;
+ index_free_full (index, (IndexFreeFunc) str_unref);
+ return okay;
}
-EXPORT char * double_array_to_string (const double * array, int count)
+EXPORT char * double_array_to_str (const double * array, int count)
{
- char * * split = g_malloc0 (sizeof (char *) * (count + 1));
+ Index * index = index_new ();
for (int i = 0; i < count; i ++)
{
- split[i] = double_to_string (array[i]);
- if (! split[i])
+ char * value = double_to_str (array[i]);
+ if (! value)
goto ERR;
+
+ index_insert (index, -1, value);
}
- char * string = g_strjoinv (",", split);
- g_strfreev (split);
+ char * string = index_to_str_list (index, ",");
+ index_free_full (index, (IndexFreeFunc) str_unref);
return string;
ERR:
- g_strfreev (split);
+ index_free_full (index, (IndexFreeFunc) str_unref);
return NULL;
}
diff --git a/src/libaudcore/audstrings.h b/src/libaudcore/audstrings.h
index c8e1035..9f6cae7 100644
--- a/src/libaudcore/audstrings.h
+++ b/src/libaudcore/audstrings.h
@@ -1,7 +1,6 @@
/*
* audstrings.h
- * Copyright 2009-2011 John Lindgren
- * Copyright 2010 William Pitcock
+ * Copyright 2009-2012 John Lindgren and William Pitcock
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -21,21 +20,86 @@
#ifndef LIBAUDCORE_STRINGS_H
#define LIBAUDCORE_STRINGS_H
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
#include <libaudcore/core.h>
+#define SPRINTF(s, ...) \
+ char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \
+ snprintf (s, sizeof s, __VA_ARGS__)
+
+#define VSPRINTF(s, f, v) \
+ va_list v##2; \
+ va_copy (v##2, v); \
+ char s[vsnprintf (NULL, 0, f, v##2) + 1]; \
+ va_end (v##2); \
+ vsnprintf (s, sizeof s, f, v)
+
+#define SCOPY(s, a) \
+ char s[strlen (a) + 1]; \
+ strcpy (s, a)
+
+#define SNCOPY(s, a, x) \
+ char s[(x) + 1]; \
+ strncpy (s, a, sizeof s - 1); \
+ s[sizeof s - 1] = 0
+
+#define SCONCAT2(s, a, b) \
+ int s##_1 = strlen (a), s##_2 = strlen (b); \
+ char s[s##_1 + s##_2 + 1]; \
+ memcpy (s, (a), s##_1); \
+ strcpy (s + s##_1, (b))
+
+#define SCONCAT3(s, a, b, c) \
+ int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c); \
+ char s[s##_1 + s##_2 + s##_3 + 1]; \
+ memcpy (s, (a), s##_1); \
+ memcpy (s + s##_1, (b), s##_2); \
+ strcpy (s + s##_1 + s##_2, (c))
+
+#define SCONCAT4(s, a, b, c, d) \
+ int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c), s##_4 = strlen (d); \
+ char s[s##_1 + s##_2 + s##_3 + s##_4 + 1]; \
+ memcpy (s, (a), s##_1); \
+ memcpy (s + s##_1, (b), s##_2); \
+ memcpy (s + s##_1 + s##_2, (c), s##_3); \
+ strcpy (s + s##_1 + s##_2 + s##_3, (d))
+
+struct _Index;
+
+/* all (char *) return values must be freed with str_unref() */
+
+char * str_printf (const char * format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+char * str_vprintf (const char * format, va_list args);
+
bool_t str_has_prefix_nocase(const char * str, const char * prefix);
bool_t str_has_suffix_nocase(const char * str, const char * suffix);
-void str_set_utf8_impl (char * (* stu_impl) (const char *),
- char * (* stuf_impl) (const char *, int, int *, int *));
-char * str_to_utf8 (const char * str);
-char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written);
+char * strstr_nocase (const char * haystack, const char * needle);
+char * strstr_nocase_utf8 (const char * haystack, const char * needle);
+
+char * str_tolower_utf8 (const char * str);
-void string_replace_char (char * string, char old_c, char new_c);
+void str_replace_char (char * string, char old_c, char new_c);
+
+void str_itoa (int x, char * buf, int bufsize);
void str_decode_percent (const char * str, int len, char * out);
void str_encode_percent (const char * str, int len, char * out);
+char * str_convert (const char * str, int len, const char * from_charset, const char * to_charset);
+char * str_from_locale (const char * str, int len);
+char * str_to_locale (const char * str, int len);
+char * str_to_utf8 (const char * str, int len);
+
+/* takes ownership of <fallbacks> and the pooled strings in it */
+void str_set_charsets (const char * region, struct _Index * fallbacks);
+
+void filename_normalize (char * filename);
+
+char * filename_build (const char * path, const char * name);
char * filename_to_uri (const char * filename);
char * uri_to_filename (const char * uri);
char * uri_to_display (const char * uri);
@@ -44,19 +108,20 @@ void uri_parse (const char * uri, const char * * base_p, const char * * ext_p,
const char * * sub_p, int * isub_p);
bool_t uri_get_extension (const char * uri, char * buf, int buflen);
-int string_compare (const char * a, const char * b);
-int string_compare_encoded (const char * a, const char * b);
+int str_compare (const char * a, const char * b);
+int str_compare_encoded (const char * a, const char * b);
-char *str_replace_fragment(char *s, int size, const char *old_str, const char *new_str);
+struct _Index * str_list_to_index (const char * list, const char * delims);
+char * index_to_str_list (struct _Index * index, const char * sep);
-bool_t string_to_int (const char * string, int * addr);
-bool_t string_to_double (const char * string, double * addr);
-char * int_to_string (int val);
-char * double_to_string (double val);
+int str_to_int (const char * string);
+double str_to_double (const char * string);
+char * int_to_str (int val);
+char * double_to_str (double val);
-bool_t string_to_int_array (const char * string, int * array, int count);
-char * int_array_to_string (const int * array, int count);
-bool_t string_to_double_array (const char * string, double * array, int count);
-char * double_array_to_string (const double * array, int count);
+bool_t str_to_int_array (const char * string, int * array, int count);
+char * int_array_to_str (const int * array, int count);
+bool_t str_to_double_array (const char * string, double * array, int count);
+char * double_array_to_str (const double * array, int count);
#endif /* LIBAUDCORE_STRINGS_H */
diff --git a/src/libaudcore/charset.c b/src/libaudcore/charset.c
new file mode 100644
index 0000000..d3c6d5f
--- /dev/null
+++ b/src/libaudcore/charset.c
@@ -0,0 +1,180 @@
+/*
+ * charset.c
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#include <iconv.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <glib.h>
+
+#ifdef USE_CHARDET
+#include <libguess/libguess.h>
+#endif
+
+#include "audstrings.h"
+#include "index.h"
+#include "tinylock.h"
+
+EXPORT char * str_convert (const char * str, int len, const char * from_charset,
+ const char * to_charset)
+{
+ iconv_t conv = iconv_open (to_charset, from_charset);
+ if (conv == (iconv_t) -1)
+ return NULL;
+
+ if (len < 0)
+ len = strlen (str);
+
+ // liberal estimate of how much space we will need
+ // are there obscure cases that require even more?
+ int maxlen = 4 * len;
+
+ char buf[maxlen + 1];
+ char * result = NULL;
+
+ size_t inbytes = len;
+ size_t outbytes = maxlen;
+ char * in = (char *) str;
+ char * out = buf;
+
+ if (iconv (conv, & in, & inbytes, & out, & outbytes) != (size_t) -1 && ! inbytes)
+ {
+ buf[maxlen - outbytes] = 0;
+ result = str_get (buf);
+ }
+
+ iconv_close (conv);
+ return result;
+}
+
+static void whine_locale (const char * str, int len, const char * dir, const char * charset)
+{
+ if (len < 0)
+ fprintf (stderr, "Cannot convert %s locale (%s): %s\n", dir, charset, str);
+ else
+ fprintf (stderr, "Cannot convert %s locale (%s): %.*s\n", dir, charset, len, str);
+}
+
+EXPORT char * str_from_locale (const char * str, int len)
+{
+ const char * charset;
+
+ if (g_get_charset (& charset))
+ {
+ /* locale is UTF-8 */
+ if (! g_utf8_validate (str, len, NULL))
+ {
+ whine_locale (str, len, "from", "UTF-8");
+ return NULL;
+ }
+
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+ }
+ else
+ {
+ char * utf8 = str_convert (str, len, charset, "UTF-8");
+ if (! utf8)
+ whine_locale (str, len, "from", charset);
+
+ return utf8;
+ }
+}
+
+EXPORT char * str_to_locale (const char * str, int len)
+{
+ const char * charset;
+
+ if (g_get_charset (& charset))
+ {
+ /* locale is UTF-8 */
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+ }
+ else
+ {
+ char * local = str_convert (str, len, "UTF-8", charset);
+ if (! local)
+ whine_locale (str, len, "to", charset);
+
+ return local;
+ }
+}
+
+static TinyRWLock settings_lock;
+static char * detect_region;
+static Index * fallback_charsets;
+
+EXPORT void str_set_charsets (const char * region, Index * fallbacks)
+{
+ tiny_lock_write (& settings_lock);
+
+ str_unref (detect_region);
+ detect_region = str_get (region);
+
+#ifdef USE_CHARDET
+ if (detect_region)
+ libguess_init ();
+#endif
+
+ if (fallback_charsets)
+ index_free_full (fallback_charsets, (IndexFreeFunc) str_unref);
+
+ fallback_charsets = fallbacks;
+
+ tiny_unlock_write (& settings_lock);
+}
+
+EXPORT char * str_to_utf8 (const char * str, int len)
+{
+ /* check whether already UTF-8 */
+ if (g_utf8_validate (str, len, NULL))
+ return (len < 0) ? str_get (str) : str_nget (str, len);
+
+ if (len < 0)
+ len = strlen (str);
+
+ char * utf8 = NULL;
+ tiny_lock_read (& settings_lock);
+
+#ifdef USE_CHARDET
+ if (detect_region)
+ {
+ /* prefer libguess-detected charset */
+ const char * detected = libguess_determine_encoding (str, len, detect_region);
+ if (detected && (utf8 = str_convert (str, len, detected, "UTF-8")))
+ goto DONE;
+ }
+#endif
+
+ if (fallback_charsets)
+ {
+ /* try user-configured fallbacks */
+ for (int i = 0; i < index_count (fallback_charsets); i ++)
+ {
+ if ((utf8 = str_convert (str, len, index_get (fallback_charsets, i), "UTF-8")))
+ goto DONE;
+ }
+ }
+
+ /* try system locale last (this one will print a warning if it fails) */
+ utf8 = str_from_locale (str, len);
+
+DONE:
+ tiny_unlock_read (& settings_lock);
+ return utf8;
+}
diff --git a/src/libaudcore/core.h b/src/libaudcore/core.h
index b92c500..f3c2615 100644
--- a/src/libaudcore/core.h
+++ b/src/libaudcore/core.h
@@ -1,6 +1,6 @@
/*
* core.h
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -43,14 +43,7 @@
#undef CLAMP
#define CLAMP(a,min,max) ((a) < (min) ? (min) : (a) > (max) ? (max) : (a))
-#define SPRINTF(s,...) \
- char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \
- snprintf (s, sizeof s, __VA_ARGS__);
-
-/* Simple sanity check to catch (1) strings that are still in use after their
- * reference count has dropped to zero and (2) strings that should have been
- * pooled but never were. If the check fails, the program is aborted. */
-#define STR_CHECK(str) do {if ((str) && (str)[-1] != '@') strpool_abort (str);} while (0)
+#define ARRAY_LEN(a) (sizeof (a) / sizeof (a)[0])
/* If the pool contains a copy of <str>, increments its reference count.
* Otherwise, adds a copy of <str> to the pool with a reference count of one.
@@ -63,7 +56,7 @@ char * str_get (const char * str);
* string already in the pool. Faster than calling str_get() a second time.
* Returns <str> for convenience. If <str> is NULL, simply returns NULL with no
* side effects. */
-char * str_ref (char * str);
+char * str_ref (const char * str);
/* Decrements the reference count of <str>, where <str> is the address of a
* string in the pool. If the reference count drops to zero, releases the
@@ -71,16 +64,18 @@ char * str_ref (char * str);
* effects. */
void str_unref (char * str);
+/* Returns the cached hash value of a pooled string (or 0 for NULL). */
+unsigned str_hash (const char * str);
+
+/* Checks whether two pooled strings are equal. Since the pool never contains
+ * duplicate strings, this is a simple pointer comparison and thus much faster
+ * than strcmp(). NULL is considered equal to NULL but not equal to any string. */
+bool_t str_equal (const char * str1, const char * str2);
+
/* Calls str_get() on the first <len> characters of <str>. If <str> has less
* than or equal to <len> characters, equivalent to str_get(). */
char * str_nget (const char * str, int len);
-/* Calls sprintf() internally, then pools the produced string with str_get(). */
-char * str_printf (const char * format, ...);
-
-/* Used by STR_CHECK; should not be called directly. */
-void strpool_abort (char * str);
-
/* Releases all memory used by the string pool. If strings remain in the pool,
* a warning may be printed to stderr in order to reveal memory leaks. */
void strpool_shutdown (void);
diff --git a/src/libaudcore/eventqueue.c b/src/libaudcore/eventqueue.c
index c2648b9..ee92c03 100644
--- a/src/libaudcore/eventqueue.c
+++ b/src/libaudcore/eventqueue.c
@@ -45,7 +45,7 @@ static bool_t event_execute (Event * event)
hook_call (event->name, event->data);
- g_free (event->name);
+ str_unref (event->name);
if (event->destroy)
event->destroy (event->data);
@@ -56,7 +56,7 @@ static bool_t event_execute (Event * event)
EXPORT void event_queue_full (int time, const char * name, void * data, void (* destroy) (void *))
{
Event * event = g_slice_new (Event);
- event->name = g_strdup (name);
+ event->name = str_get (name);
event->data = data;
event->destroy = destroy;
@@ -83,7 +83,7 @@ EXPORT void event_queue_cancel (const char * name, void * data)
g_source_remove (event->source);
events = g_list_delete_link (events, node);
- g_free (event->name);
+ str_unref (event->name);
if (event->destroy)
event->destroy (event->data);
@@ -95,3 +95,28 @@ EXPORT void event_queue_cancel (const char * name, void * data)
pthread_mutex_unlock (& mutex);
}
+
+EXPORT void event_queue_cancel_all (void)
+{
+ pthread_mutex_lock (& mutex);
+
+ GList * node = events;
+ while (node)
+ {
+ Event * event = node->data;
+ GList * next = node->next;
+
+ g_source_remove (event->source);
+ events = g_list_delete_link (events, node);
+
+ str_unref (event->name);
+ if (event->destroy)
+ event->destroy (event->data);
+
+ g_slice_free (Event, event);
+
+ node = next;
+ }
+
+ pthread_mutex_unlock (& mutex);
+}
diff --git a/src/libaudcore/hook.c b/src/libaudcore/hook.c
index 00d69c8..e9f4e39 100644
--- a/src/libaudcore/hook.c
+++ b/src/libaudcore/hook.c
@@ -1,6 +1,6 @@
/*
* hook.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2012 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/hook.h b/src/libaudcore/hook.h
index 79fe8ac..646359b 100644
--- a/src/libaudcore/hook.h
+++ b/src/libaudcore/hook.h
@@ -47,4 +47,7 @@ void event_queue_full (int time, const char * name, void * data, void (* destroy
* all hook calls matching <name> are canceled. */
void event_queue_cancel (const char * name, void * data);
+/* Cancels all pending hook calls. */
+void event_queue_cancel_all (void);
+
#endif /* LIBAUDCORE_HOOK_H */
diff --git a/src/libaudcore/index.c b/src/libaudcore/index.c
index 160a97d..c2f7f39 100644
--- a/src/libaudcore/index.c
+++ b/src/libaudcore/index.c
@@ -1,6 +1,6 @@
/*
* index.c
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,16 +17,19 @@
* the use of this software.
*/
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <glib.h>
+#include "core.h"
#include "index.h"
struct _Index {
void * * data;
int count, size;
+ void * sdata[16];
};
typedef struct {
@@ -40,19 +43,26 @@ typedef struct {
EXPORT Index * index_new (void)
{
- Index * index = g_slice_new (Index);
-
- index->data = NULL;
- index->count = 0;
- index->size = 0;
-
+ Index * index = g_new0 (Index, 1);
+ index->data = index->sdata;
+ index->size = ARRAY_LEN (index->sdata);
return index;
}
EXPORT void index_free (Index * index)
{
- g_free (index->data);
- g_slice_free (Index, index);
+ if (index->data != index->sdata)
+ g_free (index->data);
+
+ g_free (index);
+}
+
+EXPORT void index_free_full (Index * index, IndexFreeFunc func)
+{
+ for (int i = 0; i < index->count; i ++)
+ func (index->data[i]);
+
+ index_free (index);
}
EXPORT int index_count (Index * index)
@@ -62,26 +72,38 @@ EXPORT int index_count (Index * index)
EXPORT void index_allocate (Index * index, int size)
{
- if (size <= index->size)
+ assert (size >= 0);
+
+ if (index->size >= size)
return;
- if (! index->size)
+ if (index->size < 64)
index->size = 64;
- while (size > index->size)
+ while (index->size < size)
index->size <<= 1;
- index->data = g_realloc (index->data, sizeof (void *) * index->size);
+ if (index->data == index->sdata)
+ {
+ index->data = g_new (void *, index->size);
+ memcpy (index->data, index->sdata, sizeof index->sdata);
+ }
+ else
+ index->data = g_renew (void *, index->data, index->size);
}
-EXPORT void index_set (Index * index, int at, void * value)
+EXPORT void * index_get (Index * index, int at)
{
- index->data[at] = value;
+ assert (at >= 0 && at < index->count);
+
+ return index->data[at];
}
-EXPORT void * index_get (Index * index, int at)
+EXPORT void index_set (Index * index, int at, void * value)
{
- return index->data[at];
+ assert (at >= 0 && at < index->count);
+
+ index->data[at] = value;
}
static void make_room (Index * index, int at, int count)
@@ -89,62 +111,80 @@ static void make_room (Index * index, int at, int count)
index_allocate (index, index->count + count);
if (at < index->count)
- memmove (index->data + at + count, index->data + at, sizeof (void *) *
- (index->count - at));
+ memmove (index->data + at + count, index->data + at, sizeof (void *) * (index->count - at));
index->count += count;
}
EXPORT void index_insert (Index * index, int at, void * value)
{
+ if (at == -1)
+ at = index->count;
+
+ assert (at >= 0 && at <= index->count);
+
make_room (index, at, 1);
index->data[at] = value;
}
-EXPORT void index_append (Index * index, void * value)
+EXPORT void index_copy_set (Index * source, int from, Index * target, int to, int count)
{
- index_insert (index, index->count, value);
-}
+ assert (count >= 0);
+ assert (from >= 0 && from + count <= source->count);
+ assert (to >= 0 && to + count <= target->count);
-EXPORT void index_copy_set (Index * source, int from, Index * target,
- int to, int count)
-{
- memcpy (target->data + to, source->data + from, sizeof (void *) * count);
+ memmove (target->data + to, source->data + from, sizeof (void *) * count);
}
-EXPORT void index_copy_insert (Index * source, int from, Index * target,
- int to, int count)
+EXPORT void index_copy_insert (Index * source, int from, Index * target, int to, int count)
{
+ if (to == -1)
+ to = target->count;
+ if (count == -1)
+ count = source->count - from;
+
+ assert (count >= 0);
+ assert (from >= 0 && from + count <= source->count);
+ assert (to >= 0 && to <= target->count);
+
make_room (target, to, count);
- memcpy (target->data + to, source->data + from, sizeof (void *) * count);
-}
-EXPORT void index_copy_append (Index * source, int from, Index * target,
- int count)
-{
- index_copy_insert (source, from, target, target->count, count);
+ if (source == target && to <= from)
+ index_copy_set (source, from + count, target, to, count);
+ else if (source == target && to <= from + count)
+ {
+ index_copy_set (source, from, target, to, to - from);
+ index_copy_set (source, to + count, target, to + (to - from), count - (to - from));
+ }
+ else
+ index_copy_set (source, from, target, to, count);
}
-EXPORT void index_merge_insert (Index * first, int at, Index * second)
+EXPORT void index_delete (Index * index, int at, int count)
{
- index_copy_insert (second, 0, first, at, second->count);
-}
+ if (count == -1)
+ count = index->count - at;
-EXPORT void index_merge_append (Index * first, Index * second)
-{
- index_copy_insert (second, 0, first, first->count, second->count);
-}
+ assert (count >= 0);
+ assert (at >= 0 && at + count <= index->count);
-EXPORT void index_move (Index * index, int from, int to, int count)
-{
- memmove (index->data + to, index->data + from, sizeof (void *) * count);
+ index_copy_set (index, at + count, index, at, index->count - (at + count));
+
+ index->count -= count;
}
-EXPORT void index_delete (Index * index, int at, int count)
+EXPORT void index_delete_full (Index * index, int at, int count, IndexFreeFunc func)
{
- index->count -= count;
- memmove (index->data + at, index->data + at + count, sizeof (void *) *
- (index->count - at));
+ if (count == -1)
+ count = index->count - at;
+
+ assert (count >= 0);
+ assert (at >= 0 && at + count <= index->count);
+
+ for (int i = at; i < at + count; i ++)
+ func (index->data[i]);
+
+ index_delete (index, at, count);
}
static int index_compare (const void * ap, const void * bp, void * _wrapper)
@@ -156,8 +196,7 @@ static int index_compare (const void * ap, const void * bp, void * _wrapper)
EXPORT void index_sort (Index * index, int (* compare) (const void *, const void *))
{
CompareWrapper wrapper = {compare};
- g_qsort_with_data (index->data, index->count, sizeof (void *),
- index_compare, & wrapper);
+ g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare, & wrapper);
}
static int index_compare2 (const void * ap, const void * bp, void * _wrapper)
@@ -170,6 +209,5 @@ EXPORT void index_sort_with_data (Index * index, int (* compare)
(const void * a, const void * b, void * data), void * data)
{
CompareWrapper2 wrapper = {compare, data};
- g_qsort_with_data (index->data, index->count, sizeof (void *),
- index_compare2, & wrapper);
+ g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare2, & wrapper);
}
diff --git a/src/libaudcore/index.h b/src/libaudcore/index.h
index 68d21a6..3990ff8 100644
--- a/src/libaudcore/index.h
+++ b/src/libaudcore/index.h
@@ -1,6 +1,6 @@
/*
* index.h
- * Copyright 2009-2011 John Lindgren
+ * Copyright 2009-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -20,25 +20,77 @@
#ifndef LIBAUDCORE_INDEX_H
#define LIBAUDCORE_INDEX_H
+/* An "index" is an opaque structure representing a list of pointers. It is
+ * used primarily to store Audacious playlists, but can be useful for other
+ * purposes as well. */
+
struct _Index;
typedef struct _Index Index;
+typedef void (* IndexFreeFunc) (void * value);
+
+/* Returns a new, empty index. */
Index * index_new (void);
+
+/* Destroys <index>. */
void index_free (Index * index);
+
+/* Destroys <index>, first calling <func> on each pointer stored in it. */
+void index_free_full (Index * index, IndexFreeFunc func);
+
+/* Returns the number of pointers stored in <index>. */
int index_count (Index * index);
+
+/* Preallocates space to store <size> pointers in <index>. This can be used to
+ * avoid repeated memory allocations when adding pointers to <index> one by one.
+ * The value returned by index_count() does not changed until the pointers are
+ * actually added. */
void index_allocate (Index * index, int size);
-void index_set (Index * index, int at, void * value);
+
+/* Returns the value stored in <index> at position <at>. */
void * index_get (Index * index, int at);
+
+/* Stores <value> in <index> at position <at>, overwriting the previous value. */
+void index_set (Index * index, int at, void * value);
+
+/* Stores <value> in <index> at position <at>, shifting the previous value (if
+ * any) and those following it forward to make room. If <at> is -1, <value> is
+ * added to the end of <index>. */
void index_insert (Index * index, int at, void * value);
-void index_append (Index * index, void * value);
+
+/* Copies <count> pointers from <source>, starting at position <from>, to
+ * <target>, starting at position <to>. Existing pointers in <target> are
+ * overwritten. Overlapping regions are handled correctly if <source> and
+ * <target> are the same index. */
void index_copy_set (Index * source, int from, Index * target, int to, int count);
+
+/* Copies <count> pointers from <source>, starting at position <from>, to
+ * <target>, starting at position <to>. Existing pointers in <target> are
+ * shifted forward to make room. All cases are handled correctly if <source>
+ * and <target> are the same index, including the case where <to> is between
+ * <from> and <from + count>. If <to> is -1, the pointers are added to the end
+ * of <target>. If <count> is -1, copying continues until the end of <source>
+ * is reached. */
void index_copy_insert (Index * source, int from, Index * target, int to, int count);
-void index_copy_append (Index * source, int from, Index * target, int count);
-void index_merge_insert (Index * first, int at, Index * second);
-void index_merge_append (Index * first, Index * second);
-void index_move (Index * index, int from, int to, int count);
+
+/* Removes <count> pointers from <index>, start at position <at>. Any following
+ * pointers are shifted backward to close the gap. If <count> is -1, all
+ * pointers from <at> to the end of <index> are removed. */
void index_delete (Index * index, int at, int count);
+
+/* Like index_delete(), but first calls <func> on any pointer that is being
+ * removed. */
+void index_delete_full (Index * index, int at, int count, IndexFreeFunc func);
+
+/* Sort the entries in <index> using the quick-sort algorithm with the given
+ * comparison function. The return value of <compare> should follow the
+ * convention used by strcmp(): negative if (a < b), zero if (a = b), positive
+ * if (a > b). */
void index_sort (Index * index, int (* compare) (const void * a, const void * b));
+
+/* Exactly like index_sort() except that <data> is forwarded to the comparison
+ * function. This allows comparison functions whose behavior changes depending
+ * on context. */
void index_sort_with_data (Index * index, int (* compare) (const void * a,
const void * b, void * data), void * data);
diff --git a/src/libaudcore/inifile.c b/src/libaudcore/inifile.c
new file mode 100644
index 0000000..6e13a48
--- /dev/null
+++ b/src/libaudcore/inifile.c
@@ -0,0 +1,134 @@
+/*
+ * inifile.c
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#include "audstrings.h"
+#include "inifile.h"
+
+#include <glib.h>
+
+#include <string.h>
+
+static char * strskip (char * str)
+{
+ while (g_ascii_isspace (* str))
+ str ++;
+
+ return str;
+}
+
+static char * strtrim (char * str)
+{
+ int len = strlen (str);
+
+ while (len && g_ascii_isspace(str[len - 1]))
+ str[-- len] = 0;
+
+ return str;
+}
+
+EXPORT void inifile_parse (VFSFile * file,
+ void (* handle_heading) (const char * heading, void * data),
+ void (* handle_entry) (const char * key, const char * value, void * data),
+ void * data)
+{
+ int size = 512;
+ char * buf = g_new (char, size);
+
+ char * pos = buf;
+ int len = 0;
+ bool_t eof = FALSE;
+
+ while (1)
+ {
+ char * newline = memchr (pos, '\n', len);
+
+ while (! newline && ! eof)
+ {
+ memmove (buf, pos, len);
+ pos = buf;
+
+ if (len >= size - 1)
+ {
+ size <<= 1;
+ buf = g_renew (char, buf, size);
+ pos = buf;
+ }
+
+ len += vfs_fread (buf + len, 1, size - 1 - len, file);
+
+ if (len < size - 1)
+ eof = TRUE;
+
+ newline = memchr (pos, '\n', len);
+ }
+
+ if (newline)
+ * newline = 0;
+ else
+ pos[len] = 0;
+
+ char * start = strskip (pos);
+
+ switch (* start)
+ {
+ case 0:
+ case '#':
+ case ';':
+ break;
+
+ case '[':;
+ char * end = strchr (start + 1, ']');
+ if (! end)
+ break;
+
+ * end = 0;
+ handle_heading (strtrim (strskip (start + 1)), data);
+ break;
+
+ default:;
+ char * sep = strchr (start, '=');
+ if (! sep)
+ break;
+
+ * sep = 0;
+ handle_entry (strtrim (start), strtrim (strskip (sep + 1)), data);
+ break;
+ }
+
+ if (! newline)
+ break;
+
+ len -= newline + 1 - pos;
+ pos = newline + 1;
+ }
+
+ g_free (buf);
+}
+
+EXPORT bool_t inifile_write_heading (VFSFile * file, const char * heading)
+{
+ SCONCAT3 (buf, "\n[", heading, "]\n");
+ return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1);
+}
+
+EXPORT bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value)
+{
+ SCONCAT4 (buf, key, "=", value, "\n");
+ return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1);
+}
diff --git a/src/libaudcore/inifile.h b/src/libaudcore/inifile.h
new file mode 100644
index 0000000..87189c2
--- /dev/null
+++ b/src/libaudcore/inifile.h
@@ -0,0 +1,33 @@
+/*
+ * inifile.h
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#ifndef LIBAUDCORE_INIFILE_H
+#define LIBAUDCORE_INIFILE_H
+
+#include "vfs.h"
+
+void inifile_parse (VFSFile * file,
+ void (* handle_heading) (const char * heading, void * data),
+ void (* handle_entry) (const char * key, const char * value, void * data),
+ void * data);
+
+bool_t inifile_write_heading (VFSFile * file, const char * heading);
+bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value);
+
+#endif /* LIBAUDCORE_INIFILE_H */
diff --git a/src/libaudcore/multihash.c b/src/libaudcore/multihash.c
new file mode 100644
index 0000000..2be8839
--- /dev/null
+++ b/src/libaudcore/multihash.c
@@ -0,0 +1,153 @@
+/*
+ * multihash.c
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#include "multihash.h"
+
+#include <glib.h>
+
+#define INITIAL_SIZE 256 /* must be a power of two */
+
+static void resize_channel (MultihashTable * table, MultihashChannel * channel, unsigned size)
+{
+ MultihashNode * * buckets = g_new0 (MultihashNode *, size);
+
+ for (int b1 = 0; b1 < channel->size; b1 ++)
+ {
+ MultihashNode * node = channel->buckets[b1];
+
+ while (node)
+ {
+ MultihashNode * next = node->next;
+
+ unsigned hash = table->hash_func (node);
+ unsigned b2 = (hash >> MULTIHASH_SHIFT) & (size - 1);
+ MultihashNode * * node_ptr = & buckets[b2];
+
+ node->next = * node_ptr;
+ * node_ptr = node;
+
+ node = next;
+ }
+ }
+
+ g_free (channel->buckets);
+ channel->buckets = buckets;
+ channel->size = size;
+}
+
+EXPORT int multihash_lookup (MultihashTable * table, const void * data,
+ unsigned hash, MultihashAddFunc add, MultihashActionFunc action, void * state)
+{
+ unsigned c = hash & (MULTIHASH_CHANNELS - 1);
+ MultihashChannel * channel = & table->channels[c];
+
+ int status = 0;
+ tiny_lock (& channel->lock);
+
+ if (! channel->buckets)
+ {
+ if (! add)
+ goto DONE;
+
+ channel->buckets = g_new0 (MultihashNode *, INITIAL_SIZE);
+ channel->size = INITIAL_SIZE;
+ channel->used = 0;
+ }
+
+ unsigned b = (hash >> MULTIHASH_SHIFT) & (channel->size - 1);
+ MultihashNode * * node_ptr = & channel->buckets[b];
+ MultihashNode * node = * node_ptr;
+
+ while (node && ! table->match_func (node, data, hash))
+ {
+ node_ptr = & node->next;
+ node = * node_ptr;
+ }
+
+ if (node)
+ {
+ status |= MULTIHASH_FOUND;
+
+ MultihashNode * next = node->next;
+
+ if (action && action (node, state))
+ {
+ status |= MULTIHASH_REMOVED;
+
+ * node_ptr = next;
+
+ channel->used --;
+ if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE)
+ resize_channel (table, channel, channel->size >> 1);
+ }
+ }
+ else if (add && (node = add (data, hash, state)))
+ {
+ status |= MULTIHASH_ADDED;
+
+ * node_ptr = node;
+ node->next = NULL;
+
+ channel->used ++;
+ if (channel->used > channel->size)
+ resize_channel (table, channel, channel->size << 1);
+ }
+
+DONE:
+ tiny_unlock (& channel->lock);
+ return status;
+}
+
+EXPORT void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state)
+{
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ tiny_lock (& table->channels[c].lock);
+
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ {
+ MultihashChannel * channel = & table->channels[c];
+
+ for (int b = 0; b < channel->size; b ++)
+ {
+ MultihashNode * * node_ptr = & channel->buckets[b];
+ MultihashNode * node = * node_ptr;
+
+ while (node)
+ {
+ MultihashNode * next = node->next;
+
+ if (action (node, state))
+ {
+ * node_ptr = next;
+ channel->used --;
+ }
+ else
+ node_ptr = & node->next;
+
+ node = next;
+ }
+ }
+
+ if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE)
+ resize_channel (table, channel, channel->size >> 1);
+ }
+
+ for (int c = 0; c < MULTIHASH_CHANNELS; c ++)
+ tiny_unlock (& table->channels[c].lock);
+}
diff --git a/src/libaudcore/multihash.h b/src/libaudcore/multihash.h
new file mode 100644
index 0000000..1e66b21
--- /dev/null
+++ b/src/libaudcore/multihash.h
@@ -0,0 +1,95 @@
+/*
+ * multihash.h
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#ifndef LIBAUDCORE_MULTIHASH_H
+#define LIBAUDCORE_MULTIHASH_H
+
+#include <libaudcore/core.h>
+#include <libaudcore/tinylock.h>
+
+/* Multihash is a generic, thread-safe hash table. It scales well to multiple
+ * processors by the use of multiple channels, each with a separate lock. The
+ * hash value of a given node decides what channel it is stored in. Hence,
+ * different processors will tend to hit different channels, keeping lock
+ * contention to a minimum. The data structures are public in order to allow
+ * static initialization (setting all bytes to zero gives the initial state).
+ * The all-purpose lookup function enables a variety of atomic operations, such
+ * as allocating and adding a node only if not already present. */
+
+#define MULTIHASH_CHANNELS 16 /* must be a power of two */
+#define MULTIHASH_SHIFT 4 /* log (base 2) of MULTIHASH_CHANNELS */
+
+#define MULTIHASH_FOUND (1 << 0)
+#define MULTIHASH_ADDED (1 << 1)
+#define MULTIHASH_REMOVED (1 << 2)
+
+/* Skeleton structure containing internal member(s) of a multihash node. Actual
+ * node structures should be defined with MultihashNode as the first member. */
+typedef struct _MultihashNode {
+ struct _MultihashNode * next;
+} MultihashNode;
+
+/* Single channel of a multihash table. For internal use only. */
+typedef struct {
+ TinyLock lock;
+ MultihashNode * * buckets;
+ unsigned size, used;
+} MultihashChannel;
+
+/* Callback. Calculates (or retrieves) the hash value of <node>. */
+typedef unsigned (* MultihashFunc) (const MultihashNode * node);
+
+/* Callback. Returns TRUE if <node> matches <data>, otherwise FALSE. */
+typedef bool_t (* MultihashMatchFunc) (const MultihashNode * node,
+ const void * data, unsigned hash);
+
+/* Multihash table. <hash_func> and <match_func> should be initialized to
+ * functions appropriate for the type of data to be stored in the table. */
+typedef struct {
+ MultihashFunc hash_func;
+ MultihashMatchFunc match_func;
+ MultihashChannel channels[MULTIHASH_CHANNELS];
+} MultihashTable;
+
+/* Callback. May create a new node representing <data> to be added to the
+ * table. Returns the new node or NULL. */
+typedef MultihashNode * (* MultihashAddFunc) (const void * data, unsigned hash, void * state);
+
+/* Callback. Performs a user-defined action when a matching node is found.
+ * Doubles as a node removal function. Returns TRUE if <node> was freed and is
+ * to be removed, otherwise FALSE. */
+typedef bool_t (* MultihashActionFunc) (MultihashNode * node, void * state);
+
+/* All-purpose lookup function. The caller passes in the data to be looked up
+ * along with its hash value. The two callbacks are optional. <add> (if not
+ * NULL) is called if no matching node is found, and may return a new node to
+ * add to the table. <action> (if not NULL) is called if a matching node is
+ * found, and may return TRUE to remove the node from the table. <state> is
+ * forwarded to either callback. Returns the status of the lookup as a bitmask
+ * of MULTIHASH_FOUND, MULTIHASH_ADDED, and MULTIHASH_REMOVED. */
+int multihash_lookup (MultihashTable * table, const void * data, unsigned hash,
+ MultihashAddFunc add, MultihashActionFunc action, void * state);
+
+/* All-purpose iteration function. All channels of the table are locked
+ * simultaneously during the iteration to freeze the table in a consistent
+ * state. <action> is called on each node in order, and may return TRUE to
+ * remove the node from the table. <state> is forwarded to <action>. */
+void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state);
+
+#endif /* LIBAUDCORE_MULTIHASH_H */
diff --git a/src/libaudcore/strpool.c b/src/libaudcore/strpool.c
index 634a84d..71e99b3 100644
--- a/src/libaudcore/strpool.c
+++ b/src/libaudcore/strpool.c
@@ -1,6 +1,6 @@
/*
* strpool.c
- * Copyright 2011 John Lindgren
+ * Copyright 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,136 +17,237 @@
* the use of this software.
*/
-#include <glib.h>
-#include <pthread.h>
-#include <stdarg.h>
+#include <assert.h>
#include <stdio.h>
-#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include "core.h"
+#include <glib.h>
-/* Each string in the pool is allocated with five leading bytes: a 32-bit
- * reference count and a one-byte signature, the '@' character. */
+#include "audstrings.h"
+#include "multihash.h"
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-static GHashTable * table;
+#ifdef VALGRIND_FRIENDLY
-static void str_destroy (void * str)
-{
- * ((char *) str - 1) = 0;
- free ((char *) str - 5);
-}
+typedef struct {
+ unsigned hash;
+ char magic;
+ char str[];
+} StrNode;
+
+#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1)
+#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str)))
EXPORT char * str_get (const char * str)
{
if (! str)
return NULL;
- char * copy;
- pthread_mutex_lock (& mutex);
+ StrNode * node = g_malloc (NODE_SIZE_FOR (str));
+ node->magic = '@';
+ node->hash = g_str_hash (str);
- if (! table)
- table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, str_destroy);
+ strcpy (node->str, str);
+ return node->str;
+}
- if ((copy = g_hash_table_lookup (table, str)))
- {
- void * mem = copy - 5;
- (* (int32_t *) mem) ++;
- }
- else
- {
- void * mem = malloc (6 + strlen (str));
- (* (int32_t *) mem) = 1;
+EXPORT char * str_ref (const char * str)
+{
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+ assert (g_str_hash (str) == node->hash);
- copy = (char *) mem + 5;
- copy[-1] = '@';
- strcpy (copy, str);
+ return str_get (str);
+}
- g_hash_table_insert (table, copy, copy);
- }
+EXPORT void str_unref (char * str)
+{
+ if (! str)
+ return;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+ assert (g_str_hash (str) == node->hash);
- pthread_mutex_unlock (& mutex);
- return copy;
+ node->magic = 0;
+ g_free (node);
}
-EXPORT char * str_ref (char * str)
+EXPORT unsigned str_hash (const char * str)
{
if (! str)
- return NULL;
+ return 0;
- pthread_mutex_lock (& mutex);
- STR_CHECK (str);
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ return g_str_hash (str);
+}
- void * mem = str - 5;
- (* (int32_t *) mem) ++;
+EXPORT bool_t str_equal (const char * str1, const char * str2)
+{
+ assert (! str1 || NODE_OF (str1)->magic == '@');
+ assert (! str2 || NODE_OF (str2)->magic == '@');
- pthread_mutex_unlock (& mutex);
- return str;
+ return ! g_strcmp0 (str1, str2);
}
-EXPORT void str_unref (char * str)
+EXPORT void strpool_shutdown (void)
{
- if (! str)
- return;
+}
+
+#else /* ! VALGRIND_FRIENDLY */
- pthread_mutex_lock (& mutex);
- STR_CHECK (str);
+typedef struct {
+ MultihashNode node;
+ unsigned hash, refs;
+ char magic;
+ char str[];
+} StrNode;
- void * mem = str - 5;
- if (! -- (* (int32_t *) mem))
- g_hash_table_remove (table, str);
+#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1)
+#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str)))
- pthread_mutex_unlock (& mutex);
+static unsigned hash_cb (const MultihashNode * node)
+{
+ return ((const StrNode *) node)->hash;
}
-EXPORT char * str_nget (const char * str, int len)
+static bool_t match_cb (const MultihashNode * node_, const void * data, unsigned hash)
{
- if (memchr (str, 0, len))
- return str_get (str);
+ const StrNode * node = (const StrNode *) node_;
+ return data == node->str || (hash == node->hash && ! strcmp (data, node->str));
+}
- char buf[len + 1];
- memcpy (buf, str, len);
- buf[len] = 0;
+static MultihashTable strpool_table = {
+ .hash_func = hash_cb,
+ .match_func = match_cb
+};
- return str_get (buf);
+static MultihashNode * add_cb (const void * data, unsigned hash, void * state)
+{
+ StrNode * node = g_malloc (NODE_SIZE_FOR (data));
+ node->hash = hash;
+ node->refs = 1;
+ node->magic = '@';
+ strcpy (node->str, data);
+
+ * ((char * *) state) = node->str;
+ return (MultihashNode *) node;
}
-EXPORT char * str_printf (const char * format, ...)
+static bool_t ref_cb (MultihashNode * node_, void * state)
{
- va_list args;
+ StrNode * node = (StrNode *) node_;
- va_start (args, format);
- int len = vsnprintf (NULL, 0, format, args);
- va_end (args);
+ __sync_fetch_and_add (& node->refs, 1);
- char buf[len + 1];
+ * ((char * *) state) = node->str;
+ return FALSE;
+}
- va_start (args, format);
- vsnprintf (buf, sizeof buf, format, args);
- va_end (args);
+EXPORT char * str_get (const char * str)
+{
+ if (! str)
+ return NULL;
- return str_get (buf);
+ char * ret = NULL;
+ multihash_lookup (& strpool_table, str, g_str_hash (str), add_cb, ref_cb, & ret);
+ return ret;
}
-EXPORT void strpool_abort (char * str)
+EXPORT char * str_ref (const char * str)
{
- fprintf (stderr, "String not in pool: %s\n", str);
- abort ();
+ if (! str)
+ return NULL;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ __sync_fetch_and_add (& node->refs, 1);
+
+ return (char *) str;
}
-static void str_leaked (void * key, void * str, void * unused)
+static bool_t remove_cb (MultihashNode * node_, void * state)
{
- fprintf (stderr, "String not freed: %s\n", (char *) str);
+ StrNode * node = (StrNode *) node_;
+
+ if (! __sync_bool_compare_and_swap (& node->refs, 1, 0))
+ return FALSE;
+
+ node->magic = 0;
+ g_free (node);
+ return TRUE;
}
-EXPORT void strpool_shutdown (void)
+EXPORT void str_unref (char * str)
{
- if (! table)
+ if (! str)
return;
- g_hash_table_foreach (table, str_leaked, NULL);
- g_hash_table_destroy (table);
- table = NULL;
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ while (1)
+ {
+ int refs = __sync_fetch_and_add (& node->refs, 0);
+
+ if (refs > 1)
+ {
+ if (__sync_bool_compare_and_swap (& node->refs, refs, refs - 1))
+ break;
+ }
+ else
+ {
+ int status = multihash_lookup (& strpool_table, node->str,
+ node->hash, NULL, remove_cb, NULL);
+
+ assert (status & MULTIHASH_FOUND);
+ if (status & MULTIHASH_REMOVED)
+ break;
+ }
+ }
+}
+
+static bool_t leak_cb (MultihashNode * node, void * state)
+{
+ fprintf (stderr, "String leaked: %s\n", ((StrNode *) node)->str);
+ return FALSE;
+}
+
+EXPORT void strpool_shutdown (void)
+{
+ multihash_iterate (& strpool_table, leak_cb, NULL);
+}
+
+EXPORT unsigned str_hash (const char * str)
+{
+ if (! str)
+ return 0;
+
+ StrNode * node = NODE_OF (str);
+ assert (node->magic == '@');
+
+ return node->hash;
+}
+
+
+EXPORT bool_t str_equal (const char * str1, const char * str2)
+{
+ assert (! str1 || NODE_OF (str1)->magic == '@');
+ assert (! str2 || NODE_OF (str2)->magic == '@');
+
+ return str1 == str2;
+}
+
+#endif /* ! VALGRIND_FRIENDLY */
+
+EXPORT char * str_nget (const char * str, int len)
+{
+ if (memchr (str, 0, len))
+ return str_get (str);
+
+ SNCOPY (buf, str, len);
+ return str_get (buf);
}
diff --git a/src/libaudcore/tinylock.c b/src/libaudcore/tinylock.c
new file mode 100644
index 0000000..873aff1
--- /dev/null
+++ b/src/libaudcore/tinylock.c
@@ -0,0 +1,65 @@
+/*
+ * tinylock.c
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#include "tinylock.h"
+
+#ifndef VALGRIND_FRIENDLY
+
+#include <limits.h>
+#include <sched.h>
+
+#define WRITE_BIT (SHRT_MAX + 1)
+
+EXPORT void tiny_lock (TinyLock * lock)
+{
+ while (__builtin_expect (__sync_lock_test_and_set (lock, 1), 0))
+ sched_yield ();
+}
+
+EXPORT void tiny_unlock (TinyLock * lock)
+{
+ __sync_lock_release (lock);
+}
+
+EXPORT void tiny_lock_read (TinyRWLock * lock)
+{
+ while (__builtin_expect (__sync_fetch_and_add (lock, 1) & WRITE_BIT, 0))
+ {
+ __sync_fetch_and_sub (lock, 1);
+ sched_yield ();
+ }
+}
+
+EXPORT void tiny_unlock_read (TinyRWLock * lock)
+{
+ __sync_fetch_and_sub (lock, 1);
+}
+
+EXPORT void tiny_lock_write (TinyRWLock * lock)
+{
+ while (! __builtin_expect (__sync_bool_compare_and_swap (lock, 0, WRITE_BIT), 1))
+ sched_yield ();
+}
+
+EXPORT void tiny_unlock_write (TinyRWLock * lock)
+{
+ __sync_fetch_and_sub (lock, WRITE_BIT);
+}
+
+#endif /* ! VALGRIND_FRIENDLY */
diff --git a/src/libaudcore/tinylock.h.in b/src/libaudcore/tinylock.h.in
new file mode 100644
index 0000000..873af1b
--- /dev/null
+++ b/src/libaudcore/tinylock.h.in
@@ -0,0 +1,56 @@
+/*
+ * tinylock.h
+ * Copyright 2013 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#ifndef LIBAUDCORE_TINYLOCK_H
+#define LIBAUDCORE_TINYLOCK_H
+
+#if @VALGRIND_FRIENDLY@ /* VALGRIND_FRIENDLY */
+
+#include <pthread.h>
+
+typedef pthread_mutex_t TinyLock;
+typedef pthread_rwlock_t TinyRWLock;
+
+#define tiny_lock pthread_mutex_lock
+#define tiny_unlock pthread_mutex_unlock
+#define tiny_lock_read pthread_rwlock_rdlock
+#define tiny_unlock_read pthread_rwlock_unlock
+#define tiny_lock_write pthread_rwlock_wrlock
+#define tiny_unlock_write pthread_rwlock_unlock
+
+#else /* ! VALGRIND_FRIENDLY */
+
+/* TinyLock is an extremely low-overhead lock object (in terms of speed and
+ * memory usage). It makes no guarantees of fair scheduling, however. */
+
+typedef char TinyLock;
+
+void tiny_lock (TinyLock * lock);
+void tiny_unlock (TinyLock * lock);
+
+typedef unsigned short TinyRWLock;
+
+void tiny_lock_read (TinyRWLock * lock);
+void tiny_unlock_read (TinyRWLock * lock);
+void tiny_lock_write (TinyRWLock * lock);
+void tiny_unlock_write (TinyRWLock * lock);
+
+#endif /* ! VALGRIND_FRIENDLY */
+
+#endif /* LIBAUDCORE_TINYLOCK_H */
diff --git a/src/libaudcore/tuple.c b/src/libaudcore/tuple.c
index f9a65dc..7974c8b 100644
--- a/src/libaudcore/tuple.c
+++ b/src/libaudcore/tuple.c
@@ -1,6 +1,6 @@
/*
* tuple.c
- * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen,
+ * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti Hämäläinen,
* Giacomo Lozito, Eugene Zagidullin, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -18,13 +18,7 @@
* the use of this software.
*/
-/**
- * @file tuple.c
- * @brief Basic Tuple handling API.
- */
-
#include <glib.h>
-#include <pthread.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
@@ -33,8 +27,12 @@
#include <audacious/i18n.h>
#include "audstrings.h"
+#include "tinylock.h"
#include "tuple.h"
-#include "tuple_formatter.h"
+
+#if TUPLE_FIELDS > 64
+#error The current tuple implementation is limited to 64 fields
+#endif
#define BLOCK_VALS 4
@@ -61,20 +59,25 @@ struct _TupleBlock {
* metadata. This is not the same as a playlist entry, though.
*/
struct _Tuple {
- int refcount;
int64_t setmask;
TupleBlock * blocks;
- int nsubtunes; /**< Number of subtunes, if any. Values greater than 0
- mean that there are subtunes and #subtunes array
- may be set. */
int *subtunes; /**< Array of int containing subtune index numbers.
Can be NULL if indexing is linear or if
there are no subtunes. */
+ int nsubtunes; /**< Number of subtunes, if any. Values greater than 0
+ mean that there are subtunes and #subtunes array
+ may be set. */
+
+ int refcount;
+ TinyLock lock;
};
#define BIT(i) ((int64_t) 1 << (i))
+#define LOCK(t) tiny_lock ((TinyLock *) & t->lock)
+#define UNLOCK(t) tiny_unlock ((TinyLock *) & t->lock)
+
/** Ordered table of basic #Tuple field names and their #TupleValueType.
*/
static const TupleBasicType tuple_fields[TUPLE_FIELDS] = {
@@ -154,8 +157,8 @@ static const FieldDictEntry field_dict[TUPLE_FIELDS] = {
{"track-number", FIELD_TRACK_NUMBER},
{"year", FIELD_YEAR}};
-static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-
+#define VALID_FIELD(f) ((f) >= 0 && (f) < TUPLE_FIELDS)
+#define FIELD_TYPE(f) (tuple_fields[f].type)
static int field_dict_compare (const void * a, const void * b)
{
@@ -168,26 +171,18 @@ EXPORT int tuple_field_by_name (const char * name)
FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS,
sizeof (FieldDictEntry), field_dict_compare);
- if (found)
- return found->field;
-
- fprintf (stderr, "Unknown tuple field name \"%s\".\n", name);
- return -1;
+ return found ? found->field : -1;
}
EXPORT const char * tuple_field_get_name (int field)
{
- if (field < 0 || field >= TUPLE_FIELDS)
- return NULL;
-
+ g_return_val_if_fail (VALID_FIELD (field), NULL);
return tuple_fields[field].name;
}
EXPORT TupleValueType tuple_field_get_type (int field)
{
- if (field < 0 || field >= TUPLE_FIELDS)
- return TUPLE_UNKNOWN;
-
+ g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN);
return tuple_fields[field].type;
}
@@ -240,7 +235,7 @@ static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remov
return & block->vals[0];
}
-static void tuple_destroy_unlocked (Tuple * tuple)
+static void tuple_destroy (Tuple * tuple)
{
TupleBlock * next;
for (TupleBlock * block = tuple->blocks; block; block = next)
@@ -254,13 +249,10 @@ static void tuple_destroy_unlocked (Tuple * tuple)
str_unref (block->vals[i].str);
}
- memset (block, 0, sizeof (TupleBlock));
g_slice_free (TupleBlock, block);
}
- g_free(tuple->subtunes);
-
- memset (tuple, 0, sizeof (Tuple));
+ g_free (tuple->subtunes);
g_slice_free (Tuple, tuple);
}
@@ -273,11 +265,8 @@ EXPORT Tuple * tuple_new (void)
EXPORT Tuple * tuple_ref (Tuple * tuple)
{
- pthread_mutex_lock (& mutex);
+ __sync_fetch_and_add (& tuple->refcount, 1);
- tuple->refcount ++;
-
- pthread_mutex_unlock (& mutex);
return tuple;
}
@@ -286,22 +275,10 @@ EXPORT void tuple_unref (Tuple * tuple)
if (! tuple)
return;
- pthread_mutex_lock (& mutex);
-
- if (! -- tuple->refcount)
- tuple_destroy_unlocked (tuple);
-
- pthread_mutex_unlock (& mutex);
+ if (! __sync_sub_and_fetch (& tuple->refcount, 1))
+ tuple_destroy (tuple);
}
-/**
- * Sets filename/URI related fields of a #Tuple structure, based
- * on the given filename argument. The fields set are:
- * #FIELD_FILE_PATH, #FIELD_FILE_NAME and #FIELD_FILE_EXT.
- *
- * @param[in] filename Filename URI.
- * @param[in,out] tuple Tuple structure to manipulate.
- */
EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
{
const char * base, * ext, * sub;
@@ -311,34 +288,27 @@ EXPORT void tuple_set_filename (Tuple * tuple, const char * filename)
char path[base - filename + 1];
str_decode_percent (filename, base - filename, path);
- tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path);
+ tuple_set_str (tuple, FIELD_FILE_PATH, path);
char name[ext - base + 1];
str_decode_percent (base, ext - base, name);
- tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name);
+ tuple_set_str (tuple, FIELD_FILE_NAME, name);
if (ext < sub)
{
char extbuf[sub - ext];
str_decode_percent (ext + 1, sub - ext - 1, extbuf);
- tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf);
+ tuple_set_str (tuple, FIELD_FILE_EXT, extbuf);
}
if (sub[0])
- tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub);
+ tuple_set_int (tuple, FIELD_SUBSONG_ID, isub);
}
-/**
- * Creates a copy of given Tuple structure, with copied data.
- *
- * @param[in] src Tuple structure to be made a copy of.
- * @return Pointer to newly allocated Tuple.
- */
EXPORT Tuple * tuple_copy (const Tuple * old)
{
- pthread_mutex_lock (& mutex);
-
Tuple * new = tuple_new ();
+ LOCK (old);
for (int f = 0; f < TUPLE_FIELDS; f ++)
{
@@ -358,83 +328,56 @@ EXPORT Tuple * tuple_copy (const Tuple * old)
if (old->subtunes)
new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (old);
return new;
}
-/**
- * Allocates a new #Tuple structure, setting filename/URI related
- * fields based on the given filename argument by calling #tuple_set_filename.
- *
- * @param[in] filename Filename URI.
- * @return Pointer to newly allocated Tuple.
- */
-EXPORT Tuple *
-tuple_new_from_filename(const char *filename)
+EXPORT Tuple * tuple_new_from_filename (const char * filename)
{
- Tuple *tuple = tuple_new();
-
- tuple_set_filename(tuple, filename);
+ Tuple * tuple = tuple_new ();
+ tuple_set_filename (tuple, filename);
return tuple;
}
-EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x)
+EXPORT void tuple_set_int (Tuple * tuple, int field, int x)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
- return;
+ g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT);
+ LOCK (tuple);
- pthread_mutex_lock (& mutex);
-
- TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
+ TupleVal * val = lookup_val (tuple, field, TRUE, FALSE);
val->x = x;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str)
+EXPORT void tuple_set_str (Tuple * tuple, int field, const char * str)
{
- if (! str)
- {
- tuple_unset (tuple, nfield, field);
- return;
- }
+ g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING);
- if (! g_utf8_validate (str, -1, NULL))
+ if (! str)
{
- fprintf (stderr, "Invalid UTF-8: %s\n", str);
+ tuple_unset (tuple, field);
return;
}
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
- return;
-
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
- TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE);
- if (val->str)
- str_unref (val->str);
- val->str = str_get (str);
+ TupleVal * val = lookup_val (tuple, field, TRUE, FALSE);
+ str_unref (val->str);
+ val->str = str_to_utf8 (str, -1);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
+EXPORT void tuple_unset (Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS)
- return;
+ g_return_if_fail (VALID_FIELD (field));
+ LOCK (tuple);
- pthread_mutex_lock (& mutex);
-
- TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE);
+ TupleVal * val = lookup_val (tuple, field, FALSE, TRUE);
if (val)
{
- if (tuple_fields[nfield].type == TUPLE_STRING)
+ if (tuple_fields[field].type == TUPLE_STRING)
{
str_unref (val->str);
val->str = NULL;
@@ -443,96 +386,52 @@ EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field)
val->x = 0;
}
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
-/**
- * Returns #TupleValueType of given #Tuple field.
- * Desired field can be specified either by key name or if it is
- * one of basic fields, by #TupleBasicType index.
- *
- * @param[in] tuple #Tuple structure pointer.
- * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead.
- * @param[in] field String acting as key name or NULL if nfield is used.
- * @return #TupleValueType of the field or TUPLE_UNKNOWN if there was an error.
- */
-EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field)
+EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS)
- return TUPLE_UNKNOWN;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN);
+ LOCK (tuple);
- TupleValueType type = TUPLE_UNKNOWN;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ TupleValueType type = val ? FIELD_TYPE (field) : TUPLE_UNKNOWN;
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- type = tuple_fields[nfield].type;
-
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return type;
}
-EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field)
+EXPORT char * tuple_get_str (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING)
- return NULL;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING, NULL);
+ LOCK (tuple);
- char * str = NULL;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ char * str = val ? str_ref (val->str) : NULL;
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- str = str_ref (val->str);
-
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return str;
}
-/**
- * Returns integer associated to #Tuple field.
- * Desired field can be specified either by key name or if it is
- * one of basic fields, by #TupleBasicType index.
- *
- * @param[in] tuple #Tuple structure pointer.
- * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead.
- * @param[in] field String acting as key name or NULL if nfield is used.
- * @return Integer value or 0 if the field/key did not exist.
- *
- * @bug There is no way to distinguish error situations if the associated value is zero.
- */
-EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field)
+EXPORT int tuple_get_int (const Tuple * tuple, int field)
{
- if (nfield < 0)
- nfield = tuple_field_by_name (field);
- if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT)
- return 0;
-
- pthread_mutex_lock (& mutex);
+ g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT, -1);
+ LOCK (tuple);
- int x = 0;
-
- TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE);
- if (val)
- x = val->x;
+ TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE);
+ int x = val ? val->x : -1;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return x;
}
-#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \
- __VA_ARGS__)
+#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), __VA_ARGS__)
EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate,
int brate)
{
if (format)
- tuple_set_str (t, FIELD_CODEC, NULL, format);
+ tuple_set_str (t, FIELD_CODEC, format);
char buf[32];
buf[0] = 0;
@@ -544,8 +443,7 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat
else if (chans == 2)
APPEND (buf, _("Stereo"));
else
- APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels",
- chans), chans);
+ APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels", chans), chans);
if (rate > 0)
APPEND (buf, ", ");
@@ -555,15 +453,15 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat
APPEND (buf, "%d kHz", rate / 1000);
if (buf[0])
- tuple_set_str (t, FIELD_QUALITY, NULL, buf);
+ tuple_set_str (t, FIELD_QUALITY, buf);
if (brate > 0)
- tuple_set_int (t, FIELD_BITRATE, NULL, brate);
+ tuple_set_int (t, FIELD_BITRATE, brate);
}
EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
g_free (tuple->subtunes);
tuple->subtunes = NULL;
@@ -572,45 +470,27 @@ EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtu
if (subtunes)
tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes);
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
}
EXPORT int tuple_get_n_subtunes (Tuple * tuple)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
int n_subtunes = tuple->nsubtunes;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return n_subtunes;
}
EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n)
{
- pthread_mutex_lock (& mutex);
+ LOCK (tuple);
int subtune = -1;
if (n >= 0 && n < tuple->nsubtunes)
subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n;
- pthread_mutex_unlock (& mutex);
+ UNLOCK (tuple);
return subtune;
}
-
-EXPORT char * tuple_format_title (Tuple * tuple, const char * format)
-{
- static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH};
-
- char * title = tuple_formatter_process_string (tuple, format);
-
- for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++)
- {
- if (title && title[0])
- break;
-
- str_unref (title);
- title = tuple_get_str (tuple, fallbacks[i], NULL);
- }
-
- return title ? title : str_get ("");
-}
diff --git a/src/libaudcore/tuple.h b/src/libaudcore/tuple.h
index 55f3e2e..873f699 100644
--- a/src/libaudcore/tuple.h
+++ b/src/libaudcore/tuple.h
@@ -1,6 +1,6 @@
/*
* tuple.h
- * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen,
+ * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti Hämäläinen,
* Giacomo Lozito, Eugene Zagidullin, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -90,6 +90,7 @@ const char * tuple_field_get_name (int field);
TupleValueType tuple_field_get_type (int field);
typedef struct _Tuple Tuple;
+typedef struct _TupleFormatter TupleFormatter;
/* Creates a new, blank tuple with a reference count of one. */
Tuple * tuple_new (void);
@@ -105,45 +106,41 @@ void tuple_unref (Tuple * tuple);
/* Makes a copy of <tuple>. Only use tuple_copy() if you need to modify one
* copy of the tuple while not modifying the other. In most cases, tuple_ref()
* is more appropriate. */
-Tuple *tuple_copy(const Tuple *);
+Tuple * tuple_copy (const Tuple * tuple);
/* Parses the URI <filename> and sets FIELD_FILE_NAME, FIELD_FILE_PATH,
* FIELD_FILE_EXT, and FIELD_SUBSONG_ID accordingly. */
-void tuple_set_filename(Tuple *tuple, const char *filename);
+void tuple_set_filename (Tuple * tuple, const char * filename);
/* Convenience function, equivalent to calling tuple_new() and then
* tuple_set_filename(). */
-Tuple *tuple_new_from_filename(const char *filename);
+Tuple * tuple_new_from_filename (const char * filename);
/* Sets a field to the integer value <x>. */
-void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x);
+void tuple_set_int (Tuple * tuple, int field, int x);
-/* Sets the field specified by <nfield> (one of the FIELD_* constants) or
- * <field> (one of the names returned by tuple_field_get_name() to the string
- * value <str>. Only one of <nfield> or <field> may be set. If <nfield> is
- * set, <field> must be NULL; if <field> is set, <nfield> must be -1. As a
+/* Sets a field to the string value <str>. If <str> is not valid UTF-8, it will
+ * be converted according to the user's character set detection rules. As a
* special case, if <str> is NULL, the result is equivalent to calling
* tuple_unset(). */
-void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str);
+void tuple_set_str (Tuple * tuple, int field, const char * str);
/* Clears any value that a field is currently set to. */
-void tuple_unset (Tuple * tuple, int nfield, const char * field);
+void tuple_unset (Tuple * tuple, int field);
/* Returns the value type of a field, or TUPLE_UNKNOWN if the field has not been
* set to any value. */
-TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield,
- const char * field);
+TupleValueType tuple_get_value_type (const Tuple * tuple, int field);
/* Returns the string value of a field. The returned string is pooled and must
* be released with str_unref() when no longer needed. If the field has not
* been set to any value, returns NULL. */
-char * tuple_get_str (const Tuple * tuple, int nfield, const char * field);
+char * tuple_get_str (const Tuple * tuple, int field);
/* Returns the integer value of a field. If the field has not been set to any
- * value, returns 0. (In hindsight, it would have been better to return -1 in
- * this case. If you need to distinguish between a value of 0 and a field not
- * set to any value, use tuple_get_value_type().) */
-int tuple_get_int (const Tuple * tuple, int nfield, const char * field);
+ * value, returns -1. If you need to distinguish between a value of -1 and a
+ * field not set to any value, use tuple_get_value_type(). */
+int tuple_get_int (const Tuple * tuple, int field);
/* Fills in format-related fields (specifically FIELD_CODEC, FIELD_QUALITY, and
* FIELD_BITRATE). Plugins should use this function instead of setting these
@@ -168,10 +165,16 @@ int tuple_get_n_subtunes (Tuple * tuple);
/* Returns the <n>th member of the subtune array. */
int tuple_get_nth_subtune (Tuple * tuple, int n);
-/* Generates a formatted title string for <tuple> according to <format>. The
- * syntax of <format> is documented in tuple_formatter.c. The returned string
- * is pooled and must be released with str_unref() when no longer need. The
- * returned string is never NULL, though it may be the empty string. */
-char * tuple_format_title (Tuple * tuple, const char * format);
+/* Creates a tuple formatter object for the given format. The syntax of
+ * <format> is documented in tuple_formatter.c. */
+TupleFormatter * tuple_formatter_new (const char * format);
+
+/* Destroys a tuple formatter object. */
+void tuple_formatter_free (TupleFormatter * formatter);
+
+/* Generates a title string for <tuple> using the given formatter object. The
+ * returned string is pooled and must be released with str_unref() when no
+ * longer needed. Never returns NULL, but may return an empty string. */
+char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple);
#endif /* LIBAUDCORE_TUPLE_H */
diff --git a/src/libaudcore/tuple_compiler.c b/src/libaudcore/tuple_compiler.c
index 4834e17..74584c1 100644
--- a/src/libaudcore/tuple_compiler.c
+++ b/src/libaudcore/tuple_compiler.c
@@ -1,7 +1,7 @@
/*
* tuple_compiler.c
* Copyright (c) 2007 Matti 'ccr' Hämäläinen
- * Copyright (c) 2011 John Lindgren
+ * Copyright (c) 2011-2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -31,7 +31,6 @@
* currently there is just a single context, is a "global" context needed?
*/
-#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
@@ -39,18 +38,19 @@
#include <glib.h>
+#include "audstrings.h"
#include "tuple_compiler.h"
-#define MAX_STR (256)
-#define MIN_ALLOC_NODES (8)
-#define MIN_ALLOC_BUF (64)
+#define MAX_STR (256)
#define TUPLEZ_MAX_VARS (4)
+#define GET_VAR(c, i) (& g_array_index ((c), TupleEvalVar, (i)))
+
#define tuple_error(ctx, ...) fprintf (stderr, "Tuple compiler: " __VA_ARGS__)
enum {
- OP_RAW = 0, /* plain text */
- OP_FIELD, /* a field/variable */
+ OP_RAW = 0, /* plain text */
+ OP_FIELD, /* a field/variable */
OP_EXISTS,
OP_EQUALS,
OP_NOT_EQUALS,
@@ -67,42 +67,28 @@ enum {
};
struct _TupleEvalNode {
- int opcode; /* operator, see OP_ enums */
- int var[TUPLEZ_MAX_VARS]; /* tuple variable references */
- char *text; /* raw text, if any (OP_RAW) */
+ int opcode; /* operator, see OP_ enums */
+ int var[TUPLEZ_MAX_VARS]; /* tuple variable references */
+ char *text; /* raw text, if any (OP_RAW) */
struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */
};
typedef struct {
char *name;
- int type; /* Type of variable, see VAR_* */
+ int type; /* Type of variable, see VAR_* */
int defvali;
- TupleValueType ctype; /* Type of constant/def value */
+ TupleValueType ctype; /* Type of constant/def value */
- int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */
+ int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */
bool_t fieldread, fieldvalid;
char * fieldstr;
} TupleEvalVar;
-struct _TupleEvalContext {
- int nvariables;
- TupleEvalVar **variables;
-};
-
-
-static void tuple_evalctx_free_var(TupleEvalVar *var)
-{
- g_free(var->name);
- str_unref (var->fieldstr);
- g_free(var);
-}
-
-
/* Initialize an evaluation context
*/
TupleEvalContext * tuple_evalctx_new(void)
{
- return g_new0(TupleEvalContext, 1);
+ return g_array_new (FALSE, TRUE, sizeof (TupleEvalVar));
}
@@ -110,15 +96,15 @@ TupleEvalContext * tuple_evalctx_new(void)
*/
void tuple_evalctx_reset(TupleEvalContext *ctx)
{
- int i;
-
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i]) {
- ctx->variables[i]->fieldread = FALSE;
- ctx->variables[i]->fieldvalid = FALSE;
- str_unref (ctx->variables[i]->fieldstr);
- ctx->variables[i]->fieldstr = NULL;
- }
+ for (int i = 0; i < ctx->len; i ++)
+ {
+ TupleEvalVar * var = GET_VAR (ctx, i);
+
+ var->fieldread = FALSE;
+ var->fieldvalid = FALSE;
+ str_unref (var->fieldstr);
+ var->fieldstr = NULL;
+ }
}
@@ -126,57 +112,52 @@ void tuple_evalctx_reset(TupleEvalContext *ctx)
*/
void tuple_evalctx_free(TupleEvalContext *ctx)
{
- int i;
-
- if (!ctx) return;
+ for (int i = 0; i < ctx->len; i ++)
+ {
+ TupleEvalVar * var = GET_VAR (ctx, i);
- /* Deallocate variables */
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i])
- tuple_evalctx_free_var(ctx->variables[i]);
+ str_unref (var->name);
+ str_unref (var->fieldstr);
+ }
- g_free(ctx->variables);
- g_free(ctx);
+ g_array_free (ctx, TRUE);
}
+/* note: may invalidate TupleEvalVar pointers due to reallocation */
static int tuple_evalctx_add_var (TupleEvalContext * ctx, const char * name,
const int type, const TupleValueType ctype)
{
- int i;
- TupleEvalVar *tmp = g_new0(TupleEvalVar, 1);
+ int field = -1;
+
+ if (type == TUPLE_VAR_FIELD)
+ {
+ field = tuple_field_by_name (name);
+ if (field < 0)
+ return -1;
+ }
+
+ int i = ctx->len;
+ g_array_set_size (ctx, i + 1);
- tmp->name = g_strdup(name);
- tmp->type = type;
- tmp->fieldidx = -1;
- tmp->ctype = ctype;
+ TupleEvalVar * var = GET_VAR (ctx, i);
+
+ var->name = str_get (name);
+ var->type = type;
+ var->fieldidx = field;
+ var->ctype = ctype;
- /* Find fieldidx, if any */
switch (type) {
case TUPLE_VAR_FIELD:
- tmp->fieldidx = tuple_field_by_name (name);
- tmp->ctype = tuple_field_get_type (tmp->fieldidx);
+ var->ctype = tuple_field_get_type (field);
break;
case TUPLE_VAR_CONST:
if (ctype == TUPLE_INT)
- tmp->defvali = atoi(name);
+ var->defvali = atoi (name);
break;
}
- /* Find a free slot */
- for (i = 0; i < ctx->nvariables; i++)
- if (!ctx->variables[i]) {
- ctx->variables[i] = tmp;
- return i;
- }
-
- i = ctx->nvariables;
- ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES);
- memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *));
- ctx->nvariables += MIN_ALLOC_NODES;
- ctx->variables[i] = tmp;
-
return i;
}
@@ -196,12 +177,6 @@ static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node)
}
-static TupleEvalNode *tuple_evalnode_new(void)
-{
- return g_new0(TupleEvalNode, 1);
-}
-
-
void tuple_evalnode_free(TupleEvalNode *expr)
{
TupleEvalNode *curr = expr, *next;
@@ -209,12 +184,12 @@ void tuple_evalnode_free(TupleEvalNode *expr)
while (curr) {
next = curr->next;
- g_free(curr->text);
+ str_unref (curr->text);
if (curr->children)
tuple_evalnode_free(curr->children);
- g_free(curr);
+ g_slice_free (TupleEvalNode, curr);
curr = next;
}
@@ -246,11 +221,11 @@ static bool_t tc_get_item(TupleEvalContext *ctx,
}
if (*literal == FALSE) {
- while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) {
+ while (*s != '\0' && *s != tmpendch && (g_ascii_isalnum(*s) || *s == '-') && i < (max - 1)) {
buf[i++] = *(s++);
}
- if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') {
+ if (*s != tmpendch && *s != '}' && !g_ascii_isalnum(*s) && *s != '-') {
tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item);
return FALSE;
} else if (*s != tmpendch) {
@@ -286,20 +261,17 @@ static bool_t tc_get_item(TupleEvalContext *ctx,
static int tc_get_variable(TupleEvalContext *ctx, char *name, int type)
{
- int i;
TupleValueType ctype = TUPLE_UNKNOWN;
- if (name == '\0') return -1;
-
- if (isdigit(name[0])) {
+ if (g_ascii_isdigit(name[0])) {
ctype = TUPLE_INT;
type = TUPLE_VAR_CONST;
} else
ctype = TUPLE_STRING;
if (type != TUPLE_VAR_CONST) {
- for (i = 0; i < ctx->nvariables; i++)
- if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name))
+ for (int i = 0; i < ctx->len; i ++)
+ if (! strcmp (GET_VAR (ctx, i)->name, name))
return i;
}
@@ -317,7 +289,7 @@ static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res,
if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) {
(*c)++;
if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) {
- TupleEvalNode *tmp = tuple_evalnode_new();
+ TupleEvalNode *tmp = g_slice_new0 (TupleEvalNode);
(*c)++;
tmp->opcode = opcode;
@@ -374,7 +346,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
literal = FALSE;
if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
c++;
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_EXISTS;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -395,7 +367,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
if (*c == '"') {
/* String */
c++;
- } else if (isdigit(*c)) {
+ } else if (g_ascii_isdigit(*c)) {
/* Integer */
}
@@ -444,7 +416,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
literal = FALSE;
if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) {
c++;
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_IS_EMPTY;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -467,7 +439,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
/* FIXME!! FIX ME! Check for external expressions */
/* I HAS A FIELD - A field. You has it. */
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_FIELD;
if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) {
tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr);
@@ -491,7 +463,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
gssize i = 0;
c++;
- while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
+ while (*c != '\0' && (g_ascii_isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1))
tmps1[i++] = *(c++);
tmps1[i] = '\0';
@@ -516,9 +488,9 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co
}
tmps1[i] = '\0';
- tmp = tuple_evalnode_new();
+ tmp = g_slice_new0 (TupleEvalNode);
tmp->opcode = OP_RAW;
- tmp->text = g_strdup(tmps1);
+ tmp->text = str_get (tmps1);
tuple_evalnode_insert(&res, tmp);
}
}
@@ -565,16 +537,16 @@ static bool_t tf_get_fieldval (TupleEvalVar * var, const Tuple * tuple)
if (var->fieldread)
return var->fieldvalid;
- if (tuple_get_value_type (tuple, var->fieldidx, NULL) != var->ctype) {
+ if (tuple_get_value_type (tuple, var->fieldidx) != var->ctype) {
var->fieldread = TRUE;
var->fieldvalid = FALSE;
return FALSE;
}
if (var->ctype == TUPLE_INT)
- var->defvali = tuple_get_int (tuple, var->fieldidx, NULL);
+ var->defvali = tuple_get_int (tuple, var->fieldidx);
else if (var->ctype == TUPLE_STRING)
- var->fieldstr = tuple_get_str (tuple, var->fieldidx, NULL);
+ var->fieldstr = tuple_get_str (tuple, var->fieldidx);
var->fieldread = TRUE;
var->fieldvalid = TRUE;
@@ -642,7 +614,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case OP_FIELD:
- var0 = ctx->variables[curr->var[0]];
+ var0 = GET_VAR (ctx, curr->var[0]);
switch (var0->type) {
case TUPLE_VAR_FIELD:
@@ -653,7 +625,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case TUPLE_INT:
- g_snprintf (tmps, sizeof (tmps), "%d", var0->defvali);
+ str_itoa (var0->defvali, tmps, sizeof (tmps));
str = tmps;
break;
@@ -669,8 +641,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
case OP_NOT_EQUALS:
case OP_LT: case OP_LTEQ:
case OP_GT: case OP_GTEQ:
- var0 = ctx->variables[curr->var[0]];
- var1 = ctx->variables[curr->var[1]];
+ var0 = GET_VAR (ctx, curr->var[0]);
+ var1 = GET_VAR (ctx, curr->var[1]);
type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple);
type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple);
@@ -705,14 +677,15 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
break;
case OP_EXISTS:
- if (tf_get_fieldval (ctx->variables[curr->var[0]], tuple)) {
+ if (tf_get_fieldval (GET_VAR (ctx, curr->var[0]), tuple))
+ {
if (! tuple_formatter_eval_do (ctx, curr->children, tuple, out))
return FALSE;
}
break;
case OP_IS_EMPTY:
- var0 = ctx->variables[curr->var[0]];
+ var0 = GET_VAR (ctx, curr->var[0]);
if (tf_get_fieldval (var0, tuple)) {
switch (var0->ctype) {
@@ -725,9 +698,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode *
tmps2 = var0->fieldstr;
while (result && tmps2 && *tmps2 != '\0') {
- gunichar uc = g_utf8_get_char(tmps2);
- if (g_unichar_isspace(uc))
- tmps2 = g_utf8_next_char(tmps2);
+ if (g_ascii_isspace (* tmps2))
+ tmps2 ++;
else
result = FALSE;
}
diff --git a/src/libaudcore/tuple_compiler.h b/src/libaudcore/tuple_compiler.h
index 0e2ba54..1a951c1 100644
--- a/src/libaudcore/tuple_compiler.h
+++ b/src/libaudcore/tuple_compiler.h
@@ -23,12 +23,9 @@
#include <glib.h>
#include <libaudcore/tuple.h>
-struct _TupleEvalNode;
+typedef GArray TupleEvalContext;
typedef struct _TupleEvalNode TupleEvalNode;
-struct _TupleEvalContext;
-typedef struct _TupleEvalContext TupleEvalContext;
-
TupleEvalContext * tuple_evalctx_new(void);
void tuple_evalctx_reset(TupleEvalContext *ctx);
void tuple_evalctx_free(TupleEvalContext *ctx);
diff --git a/src/libaudcore/tuple_formatter.c b/src/libaudcore/tuple_formatter.c
index 04ea555..44c6e44 100644
--- a/src/libaudcore/tuple_formatter.c
+++ b/src/libaudcore/tuple_formatter.c
@@ -1,6 +1,6 @@
/*
* tuple_formatter.c
- * Copyright (c) 2007 William Pitcock
+ * Copyright (c) 2007-2013 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,11 +18,9 @@
*/
#include <glib.h>
-#include <pthread.h>
-#include <string.h>
+#include "tuple.h"
#include "tuple_compiler.h"
-#include "tuple_formatter.h"
/*
* the tuple formatter:
@@ -47,48 +45,52 @@
* - %{function:args,arg2,...}: runs function and inserts the result.
*
* everything else is treated as raw text.
- * additionally, plugins can add additional instructions and functions!
*/
-/*
- * Compile a tuplez string and cache the result.
- * This caches the result for the last string, so that
- * successive calls are sped up.
- */
+struct _TupleFormatter
+{
+ TupleEvalContext * context;
+ TupleEvalNode * node;
+ GString * buf;
+};
-char * tuple_formatter_process_string (const Tuple * tuple, const char * string)
+EXPORT TupleFormatter * tuple_formatter_new (const char * format)
{
- static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
- pthread_mutex_lock (& mutex);
+ TupleFormatter * formatter = g_slice_new (TupleFormatter);
- static char *last_string = NULL;
- static TupleEvalContext *last_ctx = NULL;
- static TupleEvalNode *last_ev = NULL;
+ formatter->context = tuple_evalctx_new ();
+ formatter->node = tuple_formatter_compile (formatter->context, format);
+ formatter->buf = g_string_sized_new (255);
- if (! last_string || strcmp (string, last_string))
- {
- g_free(last_string);
+ return formatter;
+}
+
+EXPORT void tuple_formatter_free (TupleFormatter * formatter)
+{
+ tuple_evalctx_free (formatter->context);
+ tuple_evalnode_free (formatter->node);
+ g_string_free (formatter->buf, TRUE);
- if (last_ctx != NULL)
- {
- tuple_evalctx_free(last_ctx);
- tuple_evalnode_free(last_ev);
- }
+ g_slice_free (TupleFormatter, formatter);
+}
- last_ctx = tuple_evalctx_new();
- last_string = g_strdup(string);
- last_ev = tuple_formatter_compile(last_ctx, last_string);
- }
+EXPORT char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple)
+{
+ tuple_formatter_eval (formatter->context, formatter->node, tuple, formatter->buf);
+ tuple_evalctx_reset (formatter->context);
- static GString * buf;
- if (! buf)
- buf = g_string_sized_new (255);
+ if (formatter->buf->len)
+ return str_get (formatter->buf->str);
- tuple_formatter_eval (last_ctx, last_ev, tuple, buf);
- tuple_evalctx_reset (last_ctx);
+ /* formatting failed, try fallbacks */
+ static const int fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME};
- char * result = str_get (buf->str);
+ for (int i = 0; i < ARRAY_LEN (fallbacks); i ++)
+ {
+ char * title = tuple_get_str (tuple, fallbacks[i]);
+ if (title)
+ return title;
+ }
- pthread_mutex_unlock (& mutex);
- return result;
+ return str_get ("");
}
diff --git a/src/libaudcore/vfs.c b/src/libaudcore/vfs.c
index b1c3b03..000205f 100644
--- a/src/libaudcore/vfs.c
+++ b/src/libaudcore/vfs.c
@@ -1,6 +1,6 @@
/*
* vfs.c
- * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
+ * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
* Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -18,16 +18,20 @@
* the use of this software.
*/
-#include <glib.h>
-#include <inttypes.h>
-
#include "vfs.h"
-#include "audstrings.h"
+
+#include <inttypes.h>
#include <stdio.h>
-#include <unistd.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "audstrings.h"
+#include "vfs_local.h"
#define VFS_SIG ('V' | ('F' << 8) | ('S' << 16))
@@ -123,24 +127,31 @@ vfs_fopen(const char * path,
const char * mode)
{
g_return_val_if_fail (path && mode, NULL);
- g_return_val_if_fail (lookup_func, NULL);
- const char * s = strstr (path, "://");
- g_return_val_if_fail (s, NULL);
- char scheme[s - path + 1];
- strncpy (scheme, path, s - path);
- scheme[s - path] = 0;
+ VFSConstructor * vtable = NULL;
- VFSConstructor * vtable = lookup_func (scheme);
- if (! vtable)
- return NULL;
+ if (! strncmp (path, "file://", 7))
+ vtable = & vfs_local_vtable;
+ else
+ {
+ const char * s = strstr (path, "://");
+
+ if (! s)
+ {
+ fprintf (stderr, "Invalid URI: %s\n", path);
+ return NULL;
+ }
+
+ SNCOPY (scheme, path, s - path);
+
+ if (! (vtable = lookup_func (scheme)))
+ return NULL;
+ }
const gchar * sub;
uri_parse (path, NULL, NULL, & sub, NULL);
- gchar buf[sub - path + 1];
- memcpy (buf, path, sub - path);
- buf[sub - path] = 0;
+ SNCOPY (buf, path, sub - path);
void * handle = vtable->vfs_fopen_impl (buf, mode);
if (! handle)
@@ -174,8 +185,6 @@ vfs_fclose(VFSFile * file)
ret = -1;
str_unref (file->uri);
-
- memset (file, 0, sizeof (VFSFile));
g_slice_free (VFSFile, file);
return ret;
@@ -234,12 +243,12 @@ EXPORT int64_t vfs_fwrite (const void * ptr, int64_t size, int64_t nmemb, VFSFil
EXPORT int
vfs_getc(VFSFile *file)
{
- g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
+ unsigned char c;
- if (verbose)
- logger ("VFS: <%p> getc\n", file);
+ if (vfs_fread (& c, 1, 1, file) != 1)
+ return EOF;
- return file->base->vfs_getc_impl(file);
+ return c;
}
/**
@@ -252,12 +261,10 @@ vfs_getc(VFSFile *file)
EXPORT int
vfs_ungetc(int c, VFSFile *file)
{
- g_return_val_if_fail (file && file->sig == VFS_SIG, EOF);
-
- if (verbose)
- logger ("VFS: <%p> ungetc\n", file);
+ if (vfs_fseek (file, -1, SEEK_CUR) < 0)
+ return EOF;
- return file->base->vfs_ungetc_impl(c, file);
+ return c;
}
/**
@@ -285,23 +292,13 @@ vfs_fseek(VFSFile * file,
SEEK_CUR ? "current" : whence == SEEK_SET ? "beginning" : whence ==
SEEK_END ? "end" : "invalid");
- return file->base->vfs_fseek_impl(file, offset, whence);
-}
-
-/**
- * Rewinds a VFS stream.
- *
- * @param file #VFSFile object that represents the VFS stream.
- */
-EXPORT void
-vfs_rewind(VFSFile * file)
-{
- g_return_if_fail (file && file->sig == VFS_SIG);
+ if (! file->base->vfs_fseek_impl (file, offset, whence))
+ return 0;
if (verbose)
- logger ("VFS: <%p> rewind\n", file);
+ logger ("VFS: <%p> seek failed!\n", file);
- file->base->vfs_rewind_impl(file);
+ return -1;
}
/**
@@ -415,8 +412,8 @@ vfs_file_test(const char * path, int test)
#ifdef S_ISLNK
if (test & VFS_IS_SYMLINK)
{
- struct stat st;
- if (lstat (path2, & st) < 0)
+ GStatBuf st;
+ if (g_lstat (path2, & st) < 0)
goto DONE;
if (S_ISLNK (st.st_mode))
@@ -426,8 +423,8 @@ vfs_file_test(const char * path, int test)
if (test & (VFS_IS_REGULAR | VFS_IS_DIR | VFS_IS_EXECUTABLE | VFS_EXISTS))
{
- struct stat st;
- if (stat (path2, & st) < 0)
+ GStatBuf st;
+ if (g_stat (path2, & st) < 0)
goto DONE;
if (S_ISREG (st.st_mode))
@@ -441,8 +438,7 @@ vfs_file_test(const char * path, int test)
}
DONE:
- g_free (path2);
-
+ str_unref (path2);
return ! test;
}
@@ -455,14 +451,13 @@ DONE:
EXPORT bool_t
vfs_is_writeable(const char * path)
{
- struct stat info;
+ GStatBuf info;
char * realfn = uri_to_filename (path);
- if (stat(realfn, &info) == -1)
+ if (! realfn || g_stat (realfn, & info) < 0)
return FALSE;
- g_free(realfn);
-
+ str_unref (realfn);
return (info.st_mode & S_IWUSR);
}
diff --git a/src/libaudcore/vfs.h b/src/libaudcore/vfs.h
index 006c3b7..8dcbc32 100644
--- a/src/libaudcore/vfs.h
+++ b/src/libaudcore/vfs.h
@@ -1,6 +1,6 @@
/*
* vfs.h
- * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
+ * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger,
* Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
@@ -62,15 +62,14 @@ struct _VFSConstructor {
int64_t (* vfs_fwrite_impl) (const void * ptr, int64_t size, int64_t nmemb,
VFSFile * file);
- /** A function pointer which points to a getc implementation. */
- int (* vfs_getc_impl) (VFSFile * stream);
- /** A function pointer which points to an ungetc implementation. */
- int (* vfs_ungetc_impl) (int c, VFSFile * stream);
+ void (* obs_getc) (void); // obsolete
+ void (* obs_ungetc) (void); // obsolete
/** A function pointer which points to a fseek implementation. */
int (* vfs_fseek_impl) (VFSFile * file, int64_t offset, int whence);
- /** function pointer which points to a rewind implementation. */
- void (* vfs_rewind_impl) (VFSFile * file);
+
+ void (* obs_rewind) (void); // obsolete
+
/** A function pointer which points to a ftell implementation. */
int64_t (* vfs_ftell_impl) (VFSFile * file);
/** A function pointer which points to a feof implementation. */
@@ -111,34 +110,21 @@ int vfs_fprintf (VFSFile * stream, char const * format, ...) __attribute__
((__format__ (__printf__, 2, 3)));
int vfs_fseek (VFSFile * file, int64_t offset, int whence) WARN_RETURN;
-void vfs_rewind (VFSFile * file);
int64_t vfs_ftell (VFSFile * file) WARN_RETURN;
int64_t vfs_fsize (VFSFile * file) WARN_RETURN;
int vfs_ftruncate (VFSFile * file, int64_t length) WARN_RETURN;
-bool_t vfs_fget_le16 (uint16_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_le32 (uint32_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_le64 (uint64_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be16 (uint16_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be32 (uint32_t * value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fget_be64 (uint64_t * value, VFSFile * stream) WARN_RETURN;
-
-bool_t vfs_fput_le16 (uint16_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_le32 (uint32_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_le64 (uint64_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be16 (uint16_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be32 (uint32_t value, VFSFile * stream) WARN_RETURN;
-bool_t vfs_fput_be64 (uint64_t value, VFSFile * stream) WARN_RETURN;
-
bool_t vfs_is_streaming (VFSFile * file) WARN_RETURN;
+
+/* free returned string with str_unref() */
char * vfs_get_metadata (VFSFile * file, const char * field) WARN_RETURN;
bool_t vfs_file_test (const char * path, int test) WARN_RETURN;
bool_t vfs_is_writeable (const char * path) WARN_RETURN;
bool_t vfs_is_remote (const char * path) WARN_RETURN;
-void vfs_file_get_contents (const char * filename, void * * buf, int64_t *
- size);
+void vfs_file_read_all (VFSFile * file, void * * buf, int64_t * size);
+void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size);
void vfs_set_lookup_func (VFSConstructor * (* func) (const char * scheme));
void vfs_set_verbose (bool_t verbose);
diff --git a/src/libaudcore/vfs_async.c b/src/libaudcore/vfs_async.c
index 88a47ea..d8864c6 100644
--- a/src/libaudcore/vfs_async.c
+++ b/src/libaudcore/vfs_async.c
@@ -1,6 +1,6 @@
/*
* vfs_async.c
- * Copyright 2010 William Pitcock
+ * Copyright 2010-2012 William Pitcock and John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/src/libaudcore/vfs_common.c b/src/libaudcore/vfs_common.c
index 3e64b5c..18b05c7 100644
--- a/src/libaudcore/vfs_common.c
+++ b/src/libaudcore/vfs_common.c
@@ -1,7 +1,7 @@
/*
* vfs_common.c
- * Copyright 2006-2010 Tony Vroon, William Pitcock, Maciej Grela,
- * Matti Hämäläinen, and John Lindgren
+ * Copyright 2006-2013 Tony Vroon, William Pitcock, Matti Hämäläinen, and
+ * John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -18,12 +18,12 @@
* the use of this software.
*/
-#include <glib.h>
-#include <glib/gprintf.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
+#include <glib.h>
+
+#include "audstrings.h"
#include "vfs.h"
/**
@@ -94,7 +94,7 @@ EXPORT char *vfs_fgets(char *s, int n, VFSFile *stream)
*/
EXPORT int vfs_fputs(const char *s, VFSFile *stream)
{
- gsize n = strlen(s);
+ int64_t n = strlen(s);
return ((vfs_fwrite(s, 1, n, stream) == n) ? n : EOF);
}
@@ -109,12 +109,8 @@ EXPORT int vfs_fputs(const char *s, VFSFile *stream)
*/
EXPORT int vfs_vfprintf(VFSFile *stream, char const *format, va_list args)
{
- char *string;
- int rv = g_vasprintf(&string, format, args);
- if (rv < 0) return rv;
- rv = vfs_fputs(string, stream);
- g_free(string);
- return rv;
+ VSPRINTF (buf, format, args);
+ return vfs_fputs (buf, stream);
}
/**
@@ -137,242 +133,66 @@ EXPORT int vfs_fprintf(VFSFile *stream, char const *format, ...)
return rv;
}
-/**
- * Gets contents of the file into a buffer. Buffer of filesize bytes
- * is allocated by this function as necessary.
- *
- * @param filename URI of the file to read in.
- * @param buf Pointer to a pointer variable of buffer.
- * @param size Pointer to gsize variable that will hold the amount of
- * read data e.g. filesize.
- */
-EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size)
+EXPORT void vfs_file_read_all (VFSFile * file, void * * bufp, int64_t * sizep)
{
- * buf = NULL;
- * size = 0;
-
- VFSFile *fd;
- gsize filled_size = 0, buf_size = 4096;
- unsigned char * ptr;
-
- if ((fd = vfs_fopen(filename, "rb")) == NULL)
- return;
+ char * buf = NULL;
+ int64_t size = vfs_fsize (file);
- if ((* size = vfs_fsize (fd)) >= 0)
+ if (size >= 0)
{
- * buf = g_malloc (* size);
- * size = vfs_fread (* buf, 1, * size, fd);
- goto close_handle;
+ size = MIN (size, SIZE_MAX - 1);
+ buf = g_malloc (size + 1);
+ size = vfs_fread (buf, 1, size, file);
}
+ else
+ {
+ size = 0;
- if ((*buf = g_malloc(buf_size)) == NULL)
- goto close_handle;
-
- ptr = *buf;
- while (TRUE) {
- gsize read_size = vfs_fread(ptr, 1, buf_size - filled_size, fd);
- if (read_size == 0) break;
-
- filled_size += read_size;
- ptr += read_size;
-
- if (filled_size == buf_size) {
- buf_size += 4096;
+ size_t bufsize = 4096;
+ buf = g_malloc (bufsize);
- *buf = g_realloc(*buf, buf_size);
+ size_t readsize;
+ while ((readsize = vfs_fread (buf + size, 1, bufsize - 1 - size, file)))
+ {
+ size += readsize;
- if (*buf == NULL)
- goto close_handle;
+ if (size == bufsize - 1)
+ {
+ if (bufsize > SIZE_MAX - 4096)
+ break;
- ptr = (unsigned char *) (* buf) + filled_size;
+ bufsize += 4096;
+ buf = g_realloc (buf, bufsize);
+ }
}
}
- *size = filled_size;
+ buf[size] = 0; // nul-terminate
-close_handle:
- vfs_fclose(fd);
+ * bufp = buf;
+ if (sizep)
+ * sizep = size;
}
-
/**
- * Reads an unsigned 16-bit Little Endian value from the stream
- * into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le16(uint16_t *value, VFSFile *stream)
-{
- uint16_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT16_FROM_LE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 32-bit Little Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le32(uint32_t *value, VFSFile *stream)
-{
- uint32_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT32_FROM_LE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 64-bit Little Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_le64(uint64_t *value, VFSFile *stream)
-{
- uint64_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT64_FROM_LE(tmp);
- return TRUE;
-}
-
-
-/**
- * Reads an unsigned 16-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be16(uint16_t *value, VFSFile *stream)
-{
- uint16_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT16_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 32-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be32(uint32_t *value, VFSFile *stream)
-{
- uint32_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT32_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Reads an unsigned 64-bit Big Endian value from the stream into native endian format.
- *
- * @param value Pointer to the variable to read the value into.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fget_be64(uint64_t *value, VFSFile *stream)
-{
- uint64_t tmp;
- if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1)
- return FALSE;
- *value = GUINT64_FROM_BE(tmp);
- return TRUE;
-}
-
-/**
- * Writes an unsigned 16-bit native endian value into the stream as a
- * Little Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le16(uint16_t value, VFSFile *stream)
-{
- uint16_t tmp = GUINT16_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 32-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le32(uint32_t value, VFSFile *stream)
-{
- uint32_t tmp = GUINT32_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 64-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_le64(uint64_t value, VFSFile *stream)
-{
- uint64_t tmp = GUINT64_TO_LE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
-
-/**
- * Writes an unsigned 16-bit native endian value into the stream as a
- * Big Endian value.
+ * Gets contents of the file into a buffer. Buffer of filesize bytes
+ * is allocated by this function as necessary.
*
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
+ * @param filename URI of the file to read in.
+ * @param buf Pointer to a pointer variable of buffer.
+ * @param size Pointer to gsize variable that will hold the amount of
+ * read data e.g. filesize.
*/
-EXPORT bool_t vfs_fput_be16(uint16_t value, VFSFile *stream)
+EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size)
{
- uint16_t tmp = GUINT16_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
+ * buf = NULL;
+ if (size)
+ * size = 0;
-/**
- * Writes an unsigned 32-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_be32(uint32_t value, VFSFile *stream)
-{
- uint32_t tmp = GUINT32_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
-}
+ VFSFile * file = vfs_fopen (filename, "r");
+ if (! file)
+ return;
-/**
- * Writes an unsigned 64-bit native endian value into the stream as a
- * Big Endian value.
- *
- * @param value Value to write into the stream.
- * @param stream A #VFSFile object representing the stream.
- * @return TRUE if read was succesful, FALSE if there was an error.
- */
-EXPORT bool_t vfs_fput_be64(uint64_t value, VFSFile *stream)
-{
- uint64_t tmp = GUINT64_TO_BE(value);
- return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1;
+ vfs_file_read_all (file, buf, size);
+ vfs_fclose (file);
}
diff --git a/src/libaudcore/vfs_local.c b/src/libaudcore/vfs_local.c
new file mode 100644
index 0000000..d91317a
--- /dev/null
+++ b/src/libaudcore/vfs_local.c
@@ -0,0 +1,231 @@
+/*
+ * vfs_local.c
+ * Copyright 2009-2014 John Lindgren
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions, and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions, and the following disclaimer in the documentation
+ * provided with the distribution.
+ *
+ * This software is provided "as is" and without any warranty, express or
+ * implied. In no event shall the authors be liable for any damages arising from
+ * the use of this software.
+ */
+
+#include "vfs_local.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "audstrings.h"
+
+#ifdef _WIN32
+#define fseeko fseeko64
+#define ftello ftello64
+#endif
+
+typedef enum {
+ OP_NONE,
+ OP_READ,
+ OP_WRITE
+} LocalOp;
+
+typedef struct {
+ char * path;
+ FILE * stream;
+ int64_t cached_size;
+ LocalOp last_op;
+} LocalFile;
+
+static void * local_fopen (const char * uri, const char * mode)
+{
+ char * path = uri_to_filename (uri);
+ g_return_val_if_fail (path, NULL);
+
+ const char * suffix = "";
+
+#ifdef _WIN32
+ if (! strchr (mode, 'b')) /* binary mode (Windows) */
+ suffix = "b";
+#else
+ if (! strchr (mode, 'e')) /* close on exec (POSIX) */
+ suffix = "e";
+#endif
+
+ SCONCAT2 (mode2, mode, suffix);
+
+ FILE * stream = g_fopen (path, mode2);
+
+ if (! stream)
+ {
+ perror (path);
+ str_unref (path);
+ return NULL;
+ }
+
+ LocalFile * local = g_slice_new (LocalFile);
+
+ local->path = path;
+ local->stream = stream;
+ local->cached_size = -1;
+ local->last_op = OP_NONE;
+
+ return local;
+}
+
+static int local_fclose (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ int result = fclose (local->stream);
+ if (result < 0)
+ perror (local->path);
+
+ str_unref (local->path);
+ g_slice_free (LocalFile, local);
+
+ return result;
+}
+
+static int64_t local_fread (void * ptr, int64_t size, int64_t nitems, VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op == OP_WRITE)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ local->last_op = OP_READ;
+
+ clearerr (local->stream);
+
+ int64_t result = fread (ptr, size, nitems, local->stream);
+ if (result < nitems && ferror (local->stream))
+ perror (local->path);
+
+ return result;
+}
+
+static int64_t local_fwrite (const void * ptr, int64_t size, int64_t nitems, VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op == OP_READ)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ local->last_op = OP_WRITE;
+ local->cached_size = -1;
+
+ clearerr (local->stream);
+
+ int64_t result = fwrite (ptr, size, nitems, local->stream);
+ if (result < nitems && ferror (local->stream))
+ perror (local->path);
+
+ return result;
+}
+
+static int local_fseek (VFSFile * file, int64_t offset, int whence)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ int result = fseeko (local->stream, offset, whence);
+ if (result < 0)
+ perror (local->path);
+
+ if (result == 0)
+ local->last_op = OP_NONE;
+
+ return result;
+}
+
+static int64_t local_ftell (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+ return ftello (local->stream);
+}
+
+static bool_t local_feof (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+ return feof (local->stream);
+}
+
+static int local_ftruncate (VFSFile * file, int64_t length)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->last_op != OP_NONE)
+ {
+ if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */
+ {
+ perror (local->path);
+ return 0;
+ }
+ }
+
+ int result = ftruncate (fileno (local->stream), length);
+ if (result < 0)
+ perror (local->path);
+
+ if (result == 0)
+ {
+ local->last_op = OP_NONE;
+ local->cached_size = length;
+ }
+
+ return result;
+}
+
+static int64_t local_fsize (VFSFile * file)
+{
+ LocalFile * local = vfs_get_handle (file);
+
+ if (local->cached_size >= 0)
+ return local->cached_size;
+
+ int64_t saved_pos = ftello (local->stream);
+
+ if (local_fseek (file, 0, SEEK_END) < 0)
+ return -1;
+
+ int64_t length = ftello (local->stream);
+
+ if (local_fseek (file, saved_pos, SEEK_SET) < 0)
+ return -1;
+
+ return length;
+}
+
+VFSConstructor vfs_local_vtable = {
+ .vfs_fopen_impl = local_fopen,
+ .vfs_fclose_impl = local_fclose,
+ .vfs_fread_impl = local_fread,
+ .vfs_fwrite_impl = local_fwrite,
+ .vfs_fseek_impl = local_fseek,
+ .vfs_ftell_impl = local_ftell,
+ .vfs_feof_impl = local_feof,
+ .vfs_ftruncate_impl = local_ftruncate,
+ .vfs_fsize_impl = local_fsize
+};
diff --git a/src/libaudcore/tuple_formatter.h b/src/libaudcore/vfs_local.h
index bb3052b..6fa3686 100644
--- a/src/libaudcore/tuple_formatter.h
+++ b/src/libaudcore/vfs_local.h
@@ -1,6 +1,6 @@
/*
- * tuple_formatter.h
- * Copyright (c) 2007 William Pitcock
+ * vfs_local.h
+ * Copyright 2013 John Lindgren
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -17,12 +17,11 @@
* the use of this software.
*/
-#ifndef LIBAUDCORE_TUPLE_FORMATTER_H
-#define LIBAUDCORE_TUPLE_FORMATTER_H
+#ifndef LIBAUDCORE_VFS_LOCAL_H
+#define LIBAUDCORE_VFS_LOCAL_H
-#include <libaudcore/tuple.h>
+#include "vfs.h"
-/* returned string be released with str_unref() */
-char * tuple_formatter_process_string (const Tuple * tuple, const char * string);
+extern VFSConstructor vfs_local_vtable;
-#endif /* LIBAUDCORE_TUPLE_FORMATTER_H */
+#endif /* LIBAUDCORE_VFS_LOCAL_H */