| /* $Id: misc.c,v 1.8 2010/06/05 19:02:38 fredette Exp $ */ |
| |
| /* libtme/misc.c - miscellaneous: */ |
| |
| /* |
| * Copyright (c) 2003 Matt Fredette |
| * All rights reserved. |
| * |
| * 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 and/or other materials provided with the distribution. |
| * 3. All advertising materials mentioning features or use of this software |
| * must display the following acknowledgement: |
| * This product includes software developed by Matt Fredette. |
| * 4. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <tme/common.h> |
| _TME_RCSID("$Id: misc.c,v 1.8 2010/06/05 19:02:38 fredette Exp $"); |
| |
| /* includes: */ |
| #include <tme/threads.h> |
| #include <tme/module.h> |
| #include <tme/misc.h> |
| #include <ctype.h> |
| |
| /* this initializes libtme: */ |
| int |
| tme_init(void) |
| { |
| int rc; |
| |
| /* initialize the threading system: */ |
| tme_threads_init(); |
| |
| /* initialize the module system: */ |
| tme_module_init(); |
| |
| rc = TME_OK; |
| return (rc); |
| } |
| |
| /* this tokenizes a string by whitespace: */ |
| char ** |
| tme_misc_tokenize(const char *string, |
| char comment, |
| int *_tokens_count) |
| { |
| int tokens_count; |
| int tokens_size; |
| char **tokens; |
| const char *p1; |
| const char *p2; |
| char c; |
| |
| /* we initially have no tokens: */ |
| tokens_count = 0; |
| tokens_size = 1; |
| tokens = tme_new(char *, tokens_size); |
| |
| /* tokenize this line by whitespace and watch for comments: */ |
| p1 = NULL; |
| for (p2 = string;; p2++) { |
| c = *p2; |
| |
| /* if this is a token delimiter: */ |
| if (c == '\0' |
| || isspace((unsigned char) c) |
| || c == comment) { |
| |
| /* if we had been collecting a token, it's finished: */ |
| if (p1 != NULL) { |
| |
| /* save this token: */ |
| tokens[tokens_count] = tme_dup(char, p1, (p2 - p1) + 1); |
| tokens[tokens_count][p2 - p1] = '\0'; |
| p1 = NULL; |
| |
| /* resize the tokens array if needed: */ |
| if (++tokens_count == tokens_size) { |
| tokens_size += (tokens_size >> 1) + 1; |
| tokens = tme_renew(char *, tokens, tokens_size); |
| } |
| } |
| |
| /* stop if this is the end of the line or the beginning of a |
| comment: */ |
| if (c == '\0' |
| || c == comment) { |
| break; |
| } |
| } |
| |
| /* otherwise this is part of a token: */ |
| else { |
| if (p1 == NULL) { |
| p1 = p2; |
| } |
| } |
| } |
| |
| /* done: */ |
| *_tokens_count = tokens_count; |
| tokens[tokens_count] = NULL; |
| return (tokens); |
| } |
| |
| /* this frees an array of strings: */ |
| void |
| tme_free_string_array(char **array, int length) |
| { |
| char *string; |
| int i; |
| |
| if (length < 0) { |
| for (i = 0; |
| (string = array[i]) != NULL; |
| i++) { |
| tme_free(string); |
| } |
| } |
| else { |
| for (i = 0; |
| i < length; |
| i++) { |
| tme_free(array[i]); |
| } |
| } |
| tme_free(array); |
| } |
| |
| #ifdef TME_HAVE_INT64_T |
| #define _tme_unumber_t tme_uint64_t |
| #define _tme_number_t tme_int64_t |
| #else /* !TME_HAVE_INT64_T */ |
| #define _tme_unumber_t tme_uint32_t |
| #define _tme_number_t tme_int32_t |
| #endif /* !TME_HAVE_INT64_T */ |
| |
| /* this internal function parses a number: */ |
| static _tme_unumber_t |
| _tme_misc_number_parse(const char *string, |
| _tme_unumber_t max_positive, |
| _tme_unumber_t max_negative, |
| _tme_unumber_t underflow, |
| int *_failed) |
| { |
| char c; |
| int negative; |
| unsigned int base; |
| _tme_unumber_t value, max, max_pre_shift; |
| tme_uint32_t units; |
| unsigned long digit; |
| int failed; |
| char cbuf[2], *p1; |
| |
| /* assume simple conversion failure: */ |
| *_failed = TRUE; |
| errno = 0; |
| |
| /* return simple conversion failure for a NULL string: */ |
| if (string == NULL) { |
| return (0); |
| } |
| |
| /* XXX parts of this might be ASCII-centric: */ |
| |
| /* skip leading whitespace: */ |
| for (; (c = *string) != '\0' && isspace((unsigned char) c); string++); |
| |
| /* check for a leading '-' or '+' character: */ |
| if ((negative = (c == '-')) |
| || c == '+') { |
| c = *(++string); |
| } |
| |
| /* check for a leading 0x or 0X, indicating hex, or a leading 0, |
| indicating octal. in the octal case, we don't skip the leading |
| zero, because it may be the only digit to convert: */ |
| base = 10; |
| if (c == '0') { |
| base = 8; |
| c = *(string + 1); |
| if (c == 'x' |
| || c == 'X') { |
| base = 16; |
| string += 2; |
| } |
| } |
| |
| /* determine the maximum magnitude of the converted value, and the |
| maximum magnitude past which we cannot shift it to add another |
| digit in this base without overflowing: */ |
| max = (negative ? max_negative : max_positive); |
| max_pre_shift = max / base; |
| |
| /* prepare the strtoul character buffer: */ |
| cbuf[1] = '\0'; |
| |
| /* convert characters: */ |
| value = 0; |
| for (failed = TRUE; |
| (c = *string) != '\0'; |
| failed = FALSE, string++) { |
| |
| /* stop if we can't convert this character into a digit: */ |
| cbuf[0] = c; |
| digit = strtoul(cbuf, &p1, base); |
| if (*p1 != '\0') { |
| break; |
| } |
| |
| /* return ERANGE if this digit causes an overflow: */ |
| if (value > max_pre_shift |
| || digit > (max - (value *= base))) { |
| errno = ERANGE; |
| return (negative ? underflow : max_positive); |
| } |
| value += digit; |
| } |
| |
| /* get any units: */ |
| units = 1; |
| if (!strcmp(string, "GB")) { |
| units = 1024 * 1024 * 1024; |
| } |
| else if (!strcmp(string, "MB")) { |
| units = 1024 * 1024; |
| } |
| else if (!strcmp(string, "KB")) { |
| units = 1024; |
| } |
| else if (!strcmp(string, "G")) { |
| units = 1000000000; |
| } |
| else if (!strcmp(string, "M")) { |
| units = 1000000; |
| } |
| else if (!strcmp(string, "K")) { |
| units = 1000; |
| } |
| else if (*string != '\0') { |
| failed = TRUE; |
| } |
| |
| /* return any simple conversion failure: */ |
| if (failed) { |
| return (0); |
| } |
| |
| /* return ERANGE if the units cause an overflow: */ |
| if (value > (max / units)) { |
| errno = ERANGE; |
| return (negative ? underflow : max_positive); |
| } |
| |
| /* return success: */ |
| *_failed = FALSE; |
| value *= units; |
| return (negative ? 0 - value : value); |
| } |
| |
| /* this parses an unsigned number: */ |
| _tme_unumber_t |
| tme_misc_unumber_parse_any(const char *string, |
| int *_failed) |
| { |
| _tme_unumber_t max; |
| max = 0; |
| max -= 1; |
| return (_tme_misc_number_parse(string, |
| max, |
| max, |
| max, |
| _failed)); |
| } |
| |
| /* this parses a signed number: */ |
| _tme_number_t |
| tme_misc_number_parse_any(const char *string, |
| int *_failed) |
| { |
| _tme_unumber_t max_positive; |
| _tme_unumber_t max_negative; |
| max_positive = 1; |
| max_positive = (max_positive << ((sizeof(max_positive) * 8) - 1)) - 1; |
| max_negative = max_positive + 1; |
| return (_tme_misc_number_parse(string, |
| max_positive, |
| max_negative, |
| max_negative, |
| _failed)); |
| } |
| |
| /* this parses an unsigned number that has a restricted range: */ |
| _tme_unumber_t |
| tme_misc_unumber_parse(const char *string, |
| _tme_unumber_t failure_value) |
| { |
| int failed; |
| _tme_unumber_t value; |
| value = tme_misc_unumber_parse_any(string, &failed); |
| return (failed ? failure_value : value); |
| } |
| |
| /* this parses a signed number that has a restricted range: */ |
| _tme_number_t |
| tme_misc_number_parse(const char *string, |
| _tme_number_t failure_value) |
| { |
| int failed; |
| _tme_number_t value; |
| value = tme_misc_number_parse_any(string, &failed); |
| return (failed ? failure_value : value); |
| } |
| |
| /* this returns a scaled cycle counter: */ |
| union tme_value64 |
| tme_misc_cycles_scaled(const tme_misc_cycles_scaling_t *scaling, |
| const union tme_value64 *_cycles_u) |
| { |
| tme_misc_cycles_scaling_t two_to_the_thirtysecond; |
| tme_misc_cycles_scaling_t cycles; |
| tme_misc_cycles_scaling_t cycles_scaled; |
| union tme_value64 cycles_u; |
| |
| /* make 2^32: */ |
| two_to_the_thirtysecond = 65536; |
| two_to_the_thirtysecond *= 65536; |
| |
| /* get the cycles: */ |
| #ifdef TME_HAVE_INT64_T |
| cycles |
| = (_cycles_u == NULL |
| ? tme_misc_cycles().tme_value64_uint |
| : _cycles_u->tme_value64_uint); |
| #else /* !TME_HAVE_INT64_T */ |
| if (_cycles_u == NULL) { |
| cycles_u = tme_misc_cycles(); |
| _cycles_u = &cycles_u; |
| } |
| cycles = _cycles_u->tme_value64_uint32_hi * two_to_the_thirtysecond; |
| cycles += _cycles_u->tme_value64_uint32_lo; |
| #endif /* !TME_HAVE_INT64_T */ |
| |
| /* return the scaled cycles: */ |
| cycles_scaled = cycles * *scaling; |
| #ifdef TME_HAVE_INT64_T |
| cycles_u.tme_value64_uint = cycles_scaled; |
| #else /* !TME_HAVE_INT64_T */ |
| cycles_u.tme_value64_uint32_lo = (cycles_scaled % two_to_the_thirtysecond); |
| cycles_u.tme_value64_uint32_hi = (cycles_scaled / two_to_the_thirtysecond); |
| #endif /* !TME_HAVE_INT64_T */ |
| return (cycles_u); |
| } |
| |
| /* this returns a scaling factor for the cycle counter: */ |
| void |
| tme_misc_cycles_scaling(tme_misc_cycles_scaling_t *scaling, |
| tme_uint32_t numerator, |
| tme_uint32_t denominator) |
| { |
| *scaling = numerator; |
| *scaling /= denominator; |
| } |
| |
| #ifndef TME_HAVE_MISC_CYCLES_PER_MS |
| |
| /* this returns the cycle counter rate per millisecond: */ |
| int tme_misc_cycles_per_ms_spin; |
| tme_uint32_t |
| tme_misc_cycles_per_ms(void) |
| { |
| union tme_value64 cycles_start; |
| struct timeval timeval_start; |
| union tme_value64 cycles_finish; |
| struct timeval timeval_finish; |
| tme_uint32_t ms_elapsed; |
| tme_misc_cycles_scaling_t cycles_elapsed; |
| |
| /* sample the cycle counter and the current time: */ |
| cycles_start = tme_misc_cycles(); |
| gettimeofday(&timeval_start, NULL); |
| |
| /* spin until at least a second has passed: */ |
| do { |
| tme_misc_cycles_per_ms_spin++; |
| cycles_finish = tme_misc_cycles(); |
| gettimeofday(&timeval_finish, NULL); |
| } while ((timeval_finish.tv_sec == timeval_start.tv_sec) |
| || (timeval_finish.tv_sec == (timeval_start.tv_sec + 1) |
| && timeval_finish.tv_usec < timeval_start.tv_usec)); |
| |
| /* return the approximate cycle counter rate per millisecond: */ |
| timeval_finish.tv_sec--; |
| timeval_finish.tv_usec += 1000000; |
| ms_elapsed = (timeval_finish.tv_sec - timeval_start.tv_sec) * 1000; |
| ms_elapsed += (timeval_finish.tv_usec - timeval_start.tv_usec) / 1000; |
| (void) tme_value64_sub(&cycles_finish, &cycles_start); |
| cycles_elapsed = cycles_finish.tme_value64_uint32_hi; |
| cycles_elapsed *= 65536; |
| cycles_elapsed *= 65536; |
| cycles_elapsed += cycles_finish.tme_value64_uint32_lo; |
| return (cycles_elapsed / ms_elapsed); |
| } |
| |
| #endif /* !TME_HAVE_MISC_CYCLES_PER_MS */ |
| |
| #ifndef TME_HAVE_MISC_CYCLES |
| |
| /* this returns the cycle counter: */ |
| union tme_value64 |
| tme_misc_cycles(void) |
| { |
| #ifdef TME_HAVE_INT64_T */ |
| struct timeval now; |
| tme_uint64_t cycles; |
| union tme_value64 value; |
| |
| gettimeofday(&now, NULL); |
| cycles = now.tv_sec; |
| cycles *= 1000000; |
| cycles += now.tv_usec; |
| value.tme_value64_uint = cycles; |
| return (value); |
| #else /* !TME_HAVE_INT64_T */ |
| struct timeval now; |
| tme_misc_cycles_scaling_t two_to_the_thirtysecond; |
| tme_misc_cycles_scaling_t cycles_sec; |
| tme_uint32_t cycles_lo; |
| tme_uint32_t usec; |
| union tme_value64 value; |
| |
| /* get the current time: */ |
| gettimeofday(&now, NULL); |
| |
| /* make 2^32: */ |
| two_to_the_thirtysecond = 65536; |
| two_to_the_thirtysecond *= 65536; |
| |
| /* get the seconds part of the cycles: */ |
| cycles_sec = now.tv_sec; |
| cycles_sec *= 1000000; |
| |
| /* return the cycles: */ |
| cycles_lo = (cycles_sec % two_to_the_thirtysecond); |
| usec = now.tv_usec; |
| value.tme_value64_uint32_hi |
| = (((tme_uint32_t) (cycles / two_to_the_thirtysecond)) |
| + (usec > ~cycles_lo)); |
| value.tme_value64_uint32_lo = cycles_lo + usec; |
| return (value); |
| #endif /* !TME_HAVE_INT64_T */ |
| } |
| |
| #endif /* !defined(TME_HAVE_MISC_CYCLES) */ |
| |
| /* this spins until the cycle counter reaches a threshold: */ |
| void |
| tme_misc_cycles_spin_until(const union tme_value64 *cycles_until) |
| { |
| union tme_value64 cycles; |
| |
| do { |
| cycles = tme_misc_cycles(); |
| } while (tme_value64_cmp(&cycles, <, cycles_until)); |
| } |
| |
| /* this adds two 64-bit values: */ |
| #undef tme_value64_add |
| union tme_value64 * |
| tme_value64_add(union tme_value64 *a, |
| const union tme_value64 *b) |
| { |
| tme_uint32_t a_part; |
| tme_uint32_t b_part; |
| |
| /* do the addition: */ |
| a_part = a->tme_value64_uint32_lo; |
| b_part = b->tme_value64_uint32_lo; |
| a->tme_value64_uint32_lo = a_part + b_part; |
| a->tme_value64_uint32_hi |
| = (a->tme_value64_uint32_hi |
| + (b->tme_value64_uint32_hi |
| + (b_part > ~a_part))); |
| return (a); |
| } |
| |
| /* this subtracts two 64-bit values: */ |
| #undef tme_value64_sub |
| union tme_value64 * |
| tme_value64_sub(union tme_value64 *a, |
| const union tme_value64 *b) |
| { |
| tme_uint32_t a_part; |
| tme_uint32_t b_part; |
| |
| /* do the subtraction: */ |
| a_part = a->tme_value64_uint32_lo; |
| b_part = b->tme_value64_uint32_lo; |
| a->tme_value64_uint32_lo = a_part - b_part; |
| a->tme_value64_uint32_hi |
| = (a->tme_value64_uint32_hi |
| - (b->tme_value64_uint32_hi |
| + (b_part > a_part))); |
| return (a); |
| } |