serval-dna/numeric_str.h
Andrew Bettison d9c9d465df Add approximate scaled format functions
Used to produce human-readable values in logs, reports etc.
Eg: 1,073,741,824 -> "1.07g" (SI) and "1.00G" (binary).
2016-09-21 18:47:49 +09:30

144 lines
7.2 KiB
C

/*
Serval numerical string primitives
Copyright (C) 2012-2016 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVAL_DNA__NUMERIC_STR_H__
#define __SERVAL_DNA__NUMERIC_STR_H__
#include "strbuf.h"
#include <sys/types.h> // for size_t
#include <stdint.h>
#ifndef __SERVAL_DNA__NUMERIC_STR_INLINE
# if __GNUC__ && !__GNUC_STDC_INLINE__
# define __SERVAL_DNA__NUMERIC_STR_INLINE extern inline
# else
# define __SERVAL_DNA__NUMERIC_STR_INLINE inline
# endif
#endif
/* Returns 1 if the given nul-terminated string parses successfully as an unsigned 64-bit integer.
* Returns 0 if not. This is simply a shortcut for str_to_uint32(str, 10, NULL, NULL), which is
* convenient for when a pointer to a predicate function is needed.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_is_uint64_decimal(const char *str);
/* Parse a NUL-terminated string as an integer in ASCII radix notation in the given 'base' (eg,
* base=10 means decimal).
*
* Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and
* storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then
* returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or
* if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting
* setting *afterp to point to the character where parsing failed.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_to_uint16(const char *str, unsigned base, uint16_t *result, const char **afterp);
int str_to_int32(const char *str, unsigned base, int32_t *result, const char **afterp);
int str_to_uint32(const char *str, unsigned base, uint32_t *result, const char **afterp);
int str_to_int64(const char *str, unsigned base, int64_t *result, const char **afterp);
int str_to_uint64(const char *str, unsigned base, uint64_t *result, const char **afterp);
/* Parse a length-bound string as an integer in ASCII radix notation in the given 'base' (eg,
* base=10 means decimal).
*
* Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and
* storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then
* returns 0 unless all 'strlen' characters of the string were consumed. If no integer is parsed or
* if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting
* setting *afterp to point to the character where parsing failed.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int strn_to_uint16(const char *str, size_t strlen, unsigned base, uint16_t *result, const char **afterp);
int strn_to_uint32(const char *str, size_t strlen, unsigned base, uint32_t *result, const char **afterp);
int strn_to_uint64(const char *str, size_t strlen, unsigned base, uint64_t *result, const char **afterp);
/* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means
* decimal) and scale the result by a factor given by an optional suffix "scaling" character in the
* set {kKmMgG}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' = 1e9, 'G' = * 1<<30.
*
* Return 1 if a valid scaled integer was parsed, storing the value in *result (unless result is
* NULL) and storing a pointer to the immediately succeeding character in *afterp (unless afterp is
* NULL, in which case returns 1 only if the immediately succeeding character is a nul '\0').
* Returns 0 otherwise, leaving *result and *afterp unchanged.
*
* NOTE: an argument base > 16 will cause any trailing 'g' or 'G' character to be parsed as part of
* the integer, not as a scale suffix. Ditto for base > 20 and 'k' 'K', and base > 22 and 'm' 'M'.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_to_int32_scaled(const char *str, unsigned base, int32_t *result, const char **afterp);
int str_to_uint32_scaled(const char *str, unsigned base, uint32_t *result, const char **afterp);
int str_to_int64_scaled(const char *str, unsigned base, int64_t *result, const char **afterp);
int str_to_uint64_scaled(const char *str, unsigned base, uint64_t *result, const char **afterp);
uint64_t scale_factor(const char *str, const char **afterp);
/* Append an integer value to a strbuf in ASCII decimal format, optionally scaled with a scale
* suffix character in the set {kKmMgGtTpP}: 'k' = 1e3, 'K' = 1<<10, 'm' = 1e6, 'M' = 1<<20, 'g' =
* 1e9, 'G' = * 1<<30, etc. This format is lossless because the value is only scaled if it is an
* exact multiple of the scaling factor.
*
* Eg, 1000 -> "1k"
* 1001 -> "1001"
* 1024 -> "1K"
* 1025 -> "1025"
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_uint32_scaled(strbuf sb, uint32_t value);
strbuf strbuf_append_uint64_scaled(strbuf sb, uint64_t value);
/* Append a double value to a strbuf in ASCII decimal fixed-point format with three significant
* digits, optionally scaled with either a binary scale suffix in the set {KMGTP}: 'K' = 1<<10, 'M'
* = 1<<20, 'G' = * 1<<30, etc., or an S.I. scale suffix in the set {kmgtp}: 'k' = 1e3, 'm' = 1e6,
* etc. This format is lossy because it always applies the scale, which may truncate insignificant
* digits.
*
* Eg, binary S.I.
* 1000 -> "1000" 1000 -> "1.00k"
* 1001 -> "1001" 1001 -> "1.00k"
* 1024 -> "1.00K" 1024 -> "1.02k"
* 1025 -> "1.00K" 1025 -> "1.03k"
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_double_scaled_binary(strbuf sb, double value);
strbuf strbuf_append_double_scaled_si(strbuf sb, double value);
#define alloca_double_scaled_binary(v) strbuf_str(strbuf_append_double_scaled_binary(strbuf_alloca(10), (v)))
#define alloca_double_scaled_si(v) strbuf_str(strbuf_append_double_scaled_si(strbuf_alloca(10), (v)))
/* Parse a string as a time interval (seconds) in millisecond resolution. Return the number of
* milliseconds. Valid strings are all unsigned ASCII decimal numbers with up to three digits after
* the decimal point.
*
* Return 1 if a valid interval was parsed, storing the number of milliseconds in *result (unless
* result is NULL) and storing a pointer to the immediately succeeding character in *afterp (unless
* afterp is NULL, in which case returns 1 only if the immediately succeeding character is a nul
* '\0'). Returns 0 otherwise, leaving *result and *afterp unchanged.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **afterp);
#endif // __SERVAL_DNA__NUMERIC_STR_H__