diff options
Diffstat (limited to '')
-rw-r--r-- | util/argconfig.c | 589 | ||||
-rw-r--r-- | util/argconfig.h | 189 | ||||
-rw-r--r-- | util/base64.c | 107 | ||||
-rw-r--r-- | util/base64.h | 8 | ||||
-rw-r--r-- | util/cleanup.h | 37 | ||||
-rw-r--r-- | util/crc32.c | 100 | ||||
-rw-r--r-- | util/crc32.h | 11 | ||||
-rw-r--r-- | util/json.c | 74 | ||||
-rw-r--r-- | util/json.h | 61 | ||||
-rw-r--r-- | util/mem.c | 109 | ||||
-rw-r--r-- | util/mem.h | 20 | ||||
-rw-r--r-- | util/meson.build | 16 | ||||
-rw-r--r-- | util/suffix.c | 266 | ||||
-rw-r--r-- | util/suffix.h | 45 | ||||
-rw-r--r-- | util/types.c | 204 | ||||
-rw-r--r-- | util/types.h | 60 |
16 files changed, 1896 insertions, 0 deletions
diff --git a/util/argconfig.c b/util/argconfig.c new file mode 100644 index 0000000..5ec3d6f --- /dev/null +++ b/util/argconfig.c @@ -0,0 +1,589 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2014 PMC-Sierra, 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. + * + */ + +/* + * + * Author: Logan Gunthorpe + * + * Date: Oct 23 2014 + * + * Description: + * Functions for parsing command line options. + * + */ + +#include "argconfig.h" +#include "suffix.h" + +#include <errno.h> +#include <inttypes.h> +#include <getopt.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdbool.h> +#include <locale.h> + +static const char *append_usage_str = ""; + +static int argconfig_parse_val(struct argconfig_commandline_options *s, struct option *option, + int index); + +void argconfig_append_usage(const char *str) +{ + append_usage_str = str; +} + +void print_word_wrapped(const char *s, int indent, int start, FILE *stream) +{ + const int width = 76; + const char *c, *t; + int next_space = -1; + int last_line = indent; + + while (start < indent) { + putc(' ', stream); + start++; + } + + for (c = s; *c != 0; c++) { + if (*c == '\n') + goto new_line; + + if (*c == ' ' || next_space < 0) { + next_space = 0; + for (t = c + 1; *t != 0 && *t != ' '; t++) + next_space++; + + if (((int)(c - s) + start + next_space) > (last_line - indent + width)) { + int i; +new_line: + last_line = (int) (c-s) + start; + putc('\n', stream); + for (i = 0; i < indent; i++) + putc(' ', stream); + start = indent; + continue; + } + } + putc(*c, stream); + } +} + +static void show_option(const struct argconfig_commandline_options *option) +{ + char buffer[0x1000]; + char *b = buffer; + + b += sprintf(b, " [ "); + if (option->option) { + b += sprintf(b, " --%s", option->option); + if (option->argument_type == optional_argument) + b += sprintf(b, "[=<%s>]", option->meta ? option->meta : "arg"); + if (option->argument_type == required_argument) + b += sprintf(b, "=<%s>", option->meta ? option->meta : "arg"); + if (option->short_option) + b += sprintf(b, ","); + } + if (option->short_option) { + b += sprintf(b, " -%c", option->short_option); + if (option->argument_type == optional_argument) + b += sprintf(b, " [<%s>]", option->meta ? option->meta : "arg"); + if (option->argument_type == required_argument) + b += sprintf(b, " <%s>", option->meta ? option->meta : "arg"); + } + b += sprintf(b, " ] "); + + fprintf(stderr, "%s", buffer); + if (option->help) { + print_word_wrapped("--- ", 40, b - buffer, stderr); + print_word_wrapped(option->help, 44, 44, stderr); + } + fprintf(stderr, "\n"); +} + +void argconfig_print_help(const char *program_desc, + struct argconfig_commandline_options *s) +{ + fprintf(stderr, "\033[1mUsage: %s\033[0m\n\n", + append_usage_str); + + print_word_wrapped(program_desc, 0, 0, stderr); + fprintf(stderr, "\n"); + + if (!s || !s->option) + return; + + fprintf(stderr, "\n\033[1mOptions:\033[0m\n"); + for (; s && s->option; s++) + show_option(s); +} + +static int argconfig_error(char *type, const char *opt, const char *arg) +{ + fprintf(stderr, "Expected %s argument for '%s' but got '%s'!\n", type, opt, arg); + return -EINVAL; +} + +int argconfig_parse_byte(const char *opt, const char *str, unsigned char *val) +{ + char *endptr; + unsigned long tmp = strtoul(str, &endptr, 0); + + if (errno || tmp >= 1 << 8 || str == endptr) + return argconfig_error("byte", opt, str); + + *val = tmp; + + return 0; +} + +static int argconfig_parse_type(struct argconfig_commandline_options *s, struct option *option, + int index) +{ + void *value = (void *)(char *)s->default_value; + char *endptr; + int ret = 0; + + errno = 0; /* To distinguish success/failure after strtol/stroul call */ + + switch (s->config_type) { + case CFG_STRING: + *((char **)value) = optarg; + break; + case CFG_SIZE: + *((size_t *)value) = strtol(optarg, &endptr, 0); + if (errno || optarg == endptr) + ret = argconfig_error("integer", option[index].name, optarg); + break; + case CFG_INT: + *((int *)value) = strtol(optarg, &endptr, 0); + if (errno || optarg == endptr) + ret = argconfig_error("integer", option[index].name, optarg); + break; + case CFG_BYTE: + ret = argconfig_parse_byte(option[index].name, optarg, (uint8_t *)value); + break; + case CFG_SHORT: { + unsigned long tmp = strtoul(optarg, &endptr, 0); + + if (errno || tmp >= 1 << 16 || optarg == endptr) + ret = argconfig_error("short", option[index].name, optarg); + else + *((uint16_t *)value) = tmp; + break; + } + case CFG_POSITIVE: { + uint32_t tmp = strtoul(optarg, &endptr, 0); + + if (errno || optarg == endptr) + ret = argconfig_error("word", option[index].name, optarg); + else + *((uint32_t *)value) = tmp; + break; + } + case CFG_INCREMENT: + *((int *)value) += 1; + break; + case CFG_LONG: + *((unsigned long *)value) = strtoul(optarg, &endptr, 0); + if (errno || optarg == endptr) + ret = argconfig_error("long integer", option[index].name, optarg); + break; + case CFG_LONG_SUFFIX: + ret = suffix_binary_parse(optarg, &endptr, (uint64_t *)value); + if (ret) + argconfig_error("long suffixed integer", option[index].name, optarg); + break; + case CFG_DOUBLE: + *((double *)value) = strtod(optarg, &endptr); + if (errno || optarg == endptr) + ret = argconfig_error("float", option[index].name, optarg); + break; + case CFG_FLAG: + *((bool *)value) = true; + break; + default: + break; + } + + return ret; +} + +static int argconfig_get_val_len(struct argconfig_opt_val *opt_val, const char *str) +{ + struct argconfig_opt_val *v; + int len; + int match; + + for (len = 1; len <= strlen(str); len++) { + match = 0; + for (v = opt_val; v && v->str; v++) { + if (!strncasecmp(str, v->str, len)) + match++; + } + if (match == 1) + break; + } + + return len; +} + +static int argconfig_set_opt_val(enum argconfig_types type, union argconfig_val *opt_val, void *val) +{ + switch (type) { + case CFG_FLAG: + *(bool *)val = opt_val->bool_val; + break; + case CFG_LONG_SUFFIX: + *(uint64_t *)val = opt_val->long_suffix; + break; + case CFG_POSITIVE: + *(uint32_t *)val = opt_val->positive; + break; + case CFG_INT: + *(int *)val = opt_val->int_val; + break; + case CFG_LONG: + *(unsigned long *)val = opt_val->long_val; + break; + case CFG_DOUBLE: + *(double *)val = opt_val->double_val; + break; + case CFG_BYTE: + *(uint8_t *)val = opt_val->byte; + break; + case CFG_SHORT: + *(uint16_t *)val = opt_val->short_val; + break; + case CFG_INCREMENT: + *(int *)val = opt_val->increment; + break; + case CFG_STRING: + *(char **)val = opt_val->string; + break; + default: + break; + } + + return 0; +} + +static int argconfig_parse_val(struct argconfig_commandline_options *s, struct option *option, + int index) +{ + const char *str = optarg; + void *val = s->default_value; + int len = strlen(optarg); + struct argconfig_opt_val *v; + int val_len; + + for (v = s->opt_val; v && v->str; v++) { + val_len = argconfig_get_val_len(s->opt_val, v->str); + if (strncasecmp(str, v->str, len > val_len ? len : val_len)) + continue; + return argconfig_set_opt_val(v->type, &v->val, val); + } + + return argconfig_parse_type(s, option, index); +} + +static bool argconfig_check_human_readable(struct argconfig_commandline_options *s) +{ + for (; s && s->option; s++) { + if (!strcmp(s->option, "human-readable") && s->config_type == CFG_FLAG) + return s->seen; + } + + return false; +} + +int argconfig_parse(int argc, char *argv[], const char *program_desc, + struct argconfig_commandline_options *options) +{ + char *short_opts; + struct option *long_opts; + struct argconfig_commandline_options *s; + int c, option_index = 0, short_index = 0, options_count = 0; + int ret = 0; + + errno = 0; + for (s = options; s->option; s++) + options_count++; + + long_opts = calloc(1, sizeof(struct option) * (options_count + 3)); + short_opts = calloc(1, sizeof(*short_opts) * (options_count * 3 + 5)); + + if (!long_opts || !short_opts) { + fprintf(stderr, "failed to allocate memory for opts: %s\n", strerror(errno)); + ret = -errno; + goto out; + } + + for (s = options; s->option && option_index < options_count; s++) { + if (s->short_option) { + short_opts[short_index++] = s->short_option; + if (s->argument_type == required_argument || + s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + if (s->argument_type == optional_argument) + short_opts[short_index++] = ':'; + } + if (s->option && strlen(s->option)) { + long_opts[option_index].name = s->option; + long_opts[option_index].has_arg = s->argument_type; + } + s->seen = false; + option_index++; + } + + long_opts[option_index].name = "help"; + long_opts[option_index].val = 'h'; + + short_opts[short_index++] = '?'; + short_opts[short_index] = 'h'; + + optind = 0; + while ((c = getopt_long_only(argc, argv, short_opts, long_opts, &option_index)) != -1) { + if (c) { + if (c == '?' || c == 'h') { + argconfig_print_help(program_desc, options); + ret = -EINVAL; + break; + } + for (option_index = 0; option_index < options_count; option_index++) { + if (c == options[option_index].short_option) + break; + } + if (option_index == options_count) + continue; + } + + s = &options[option_index]; + s->seen = true; + + if (!s->default_value) + continue; + + if (s->opt_val) + ret = argconfig_parse_val(s, long_opts, option_index); + else + ret = argconfig_parse_type(s, long_opts, option_index); + if (ret) + break; + } + + if (!argconfig_check_human_readable(options)) + setlocale(LC_ALL, "C"); + +out: + free(short_opts); + free(long_opts); + return ret; +} + +int argconfig_parse_comma_sep_array(char *string, int *val, unsigned int max_length) +{ + int ret = 0; + unsigned long v; + char *tmp; + char *p; + + if (!string || !strlen(string)) + return 0; + + tmp = strtok(string, ","); + if (!tmp) + return 0; + + v = strtoul(tmp, &p, 0); + if (*p != 0) + return -1; + if (v > UINT_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; + + ret++; + while (1) { + tmp = strtok(NULL, ","); + + if (tmp == NULL) + return ret; + + if (ret >= max_length) + return -1; + + v = strtoul(tmp, &p, 0); + if (*p != 0) + return -1; + if (v > UINT_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; + ret++; + } +} + +int argconfig_parse_comma_sep_array_short(char *string, unsigned short *val, + unsigned int max_length) +{ + int ret = 0; + unsigned long v; + char *tmp; + char *p; + + if (!string || !strlen(string)) + return 0; + + tmp = strtok(string, ","); + if (!tmp) + return 0; + + v = strtoul(tmp, &p, 0); + if (*p != 0) + return -1; + if (v > UINT16_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; + ret++; + + while (1) { + tmp = strtok(NULL, ","); + if (tmp == NULL) + return ret; + + if (ret >= max_length) + return -1; + + v = strtoul(tmp, &p, 0); + if (*p != 0) + return -1; + if (v > UINT16_MAX) { + fprintf(stderr, "%s out of range\n", tmp); + return -1; + } + val[ret] = v; + ret++; + } +} + +int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *val, + unsigned int max_length) +{ + int ret = 0; + char *tmp; + char *p; + + if (!string || !strlen(string)) + return 0; + + tmp = strtok(string, ","); + if (tmp == NULL) + return 0; + + val[ret] = strtoll(tmp, &p, 0); + if (*p != 0) + return -1; + ret++; + while (1) { + tmp = strtok(NULL, ","); + + if (tmp == NULL) + return ret; + + if (ret >= max_length) + return -1; + + val[ret] = strtoll(tmp, &p, 0); + if (*p != 0) + return -1; + ret++; + } +} + +#define DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(size) \ +int argconfig_parse_comma_sep_array_u##size(char *string, \ + __u##size *val, \ + unsigned int max_length) \ +{ \ + int ret = 0; \ + uintmax_t v; \ + char *tmp; \ + char *p; \ + \ + if (!string || !strlen(string)) \ + return 0; \ + \ + tmp = strtok(string, ","); \ + if (!tmp) \ + return 0; \ + \ + v = strtoumax(tmp, &p, 0); \ + if (*p != 0) \ + return -1; \ + if (v > UINT##size##_MAX) { \ + fprintf(stderr, "%s out of range\n", tmp); \ + return -1; \ + } \ + val[ret] = v; \ + \ + ret++; \ + while (1) { \ + tmp = strtok(NULL, ","); \ + \ + if (tmp == NULL) \ + return ret; \ + \ + if (ret >= max_length) \ + return -1; \ + \ + v = strtoumax(tmp, &p, 0); \ + if (*p != 0) \ + return -1; \ + if (v > UINT##size##_MAX) { \ + fprintf(stderr, "%s out of range\n", tmp); \ + return -1; \ + } \ + val[ret] = v; \ + ret++; \ + } \ +} + +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(16); +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(32); +DEFINE_ARGCONFIG_PARSE_COMMA_SEP_ARRAY_UINT_FUNC(64); + +bool argconfig_parse_seen(struct argconfig_commandline_options *s, + const char *option) +{ + for (; s && s->option; s++) { + if (!strcmp(s->option, option)) + return s->seen; + } + + return false; +} diff --git a/util/argconfig.h b/util/argconfig.h new file mode 100644 index 0000000..2a04a32 --- /dev/null +++ b/util/argconfig.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Copyright 2014 PMC-Sierra, 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. + * + */ + +/* + * + * Author: Logan Gunthorpe <logang@deltatee.com> + * Logan Gunthorpe + * + * Date: Oct 23 2014 + * + * Description: + * Header file for argconfig.c + * + */ + +#ifndef argconfig_H +#define argconfig_H + +#include <string.h> +#include <getopt.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> + +#include <linux/types.h> + +enum argconfig_types { + CFG_FLAG, + CFG_STRING, + CFG_INT, + CFG_SIZE, + CFG_LONG, + CFG_LONG_SUFFIX, + CFG_DOUBLE, + CFG_BYTE, + CFG_SHORT, + CFG_POSITIVE, + CFG_INCREMENT, +}; + +#define OPT_ARGS(n) \ + struct argconfig_commandline_options n[] + +#define OPT_END() { NULL } + +#define OPT_FLAG(l, s, v, d, ...) \ + {l, s, NULL, CFG_FLAG, v, no_argument, d, false, __VA_ARGS__} + +#define OPT_SUFFIX(l, s, v, d, ...) \ + {l, s, "IONUM", CFG_LONG_SUFFIX, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_UINT(l, s, v, d, ...) \ + {l, s, "NUM", CFG_POSITIVE, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_INT(l, s, v, d, ...) \ + {l, s, "NUM", CFG_INT, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_LONG(l, s, v, d, ...) \ + {l, s, "NUM", CFG_LONG, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_DOUBLE(l, s, v, d, ...) \ + {l, s, "NUM", CFG_DOUBLE, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_BYTE(l, s, v, d, ...) \ + {l, s, "NUM", CFG_BYTE, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_SHRT(l, s, v, d, ...) \ + {l, s, "NUM", CFG_SHORT, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_INCR(l, s, v, d, ...) \ + {l, s, "NUM", CFG_INCREMENT, v, no_argument, d, false, __VA_ARGS__} + +#define OPT_STRING(l, s, m, v, d, ...) \ + {l, s, m, CFG_STRING, v, required_argument, d, false, __VA_ARGS__} + +#define OPT_FMT(l, s, v, d, ...) OPT_STRING(l, s, "FMT", v, d, __VA_ARGS__) +#define OPT_FILE(l, s, v, d, ...) OPT_STRING(l, s, "FILE", v, d, __VA_ARGS__) +#define OPT_LIST(l, s, v, d, ...) OPT_STRING(l, s, "LIST", v, d, __VA_ARGS__) +#define OPT_STR(l, s, v, d, ...) OPT_STRING(l, s, "STRING", v, d, __VA_ARGS__) + +#define OPT_VALS(n) \ + struct argconfig_opt_val n[] + +#define VAL_END() { NULL } + +#define VAL_FLAG(s, l, v) \ + {s, CFG_FLAG, .val.flag = v} + +#define VAL_LONG_SUFFIX(s, v) \ + {s, CFG_LONG_SUFFIX, .val.long_suffix = v} + +#define VAL_UINT(s, v) \ + {s, CFG_POSITIVE, v} + +#define VAL_INT(s, v) \ + {s, CFG_INT, .val.int_val = v} + +#define VAL_LONG(s, v) \ + {s, CFG_LONG, .val.long_val = v} + +#define VAL_DOUBLE(s, v) \ + {s, CFG_DOUBLE, .val.double_val = v} + +#define VAL_BYTE(s, v) \ + {s, CFG_BYTE, .val.byte = v} + +#define VAL_SHRT(s, v) \ + {s, CFG_SHORT, .val.short_val = v} + +#define VAL_INCR(s, v) \ + {s, CFG_INCREMENT, .val.increment = v} + +#define VAL_STRING(s, m, v) \ + {s, CFG_STRING, .val.string = v} + +union argconfig_val { + char *string; + size_t size; + int int_val; + int bool_val; + uint8_t byte; + uint16_t short_val; + uint32_t positive; + int increment; + unsigned long long_val; + uint64_t long_suffix; + double double_val; + bool flag; +}; + +struct argconfig_opt_val { + const char *str; + enum argconfig_types type; + union argconfig_val val; +}; + +struct argconfig_commandline_options { + const char *option; + const char short_option; + const char *meta; + enum argconfig_types config_type; + void *default_value; + int argument_type; + const char *help; + bool seen; + struct argconfig_opt_val *opt_val; +}; + +void argconfig_append_usage(const char *str); +void argconfig_print_help(const char *program_desc, + struct argconfig_commandline_options *options); +int argconfig_parse(int argc, char *argv[], const char *program_desc, + struct argconfig_commandline_options *options); +int argconfig_parse_comma_sep_array(char *string, int *ret, unsigned int max_length); +int argconfig_parse_comma_sep_array_short(char *string, unsigned short *ret, + unsigned int max_length); +int argconfig_parse_comma_sep_array_long(char *string, unsigned long long *ret, + unsigned int max_length); +int argconfig_parse_comma_sep_array_u16(char *string, __u16 *val, + unsigned int max_length); +int argconfig_parse_comma_sep_array_u32(char *string, __u32 *val, + unsigned int max_length); +int argconfig_parse_comma_sep_array_u64(char *string, __u64 *val, + unsigned int max_length); +int argconfig_parse_byte(const char *opt, const char *str, unsigned char *val); + +void print_word_wrapped(const char *s, int indent, int start, FILE *stream); +bool argconfig_parse_seen(struct argconfig_commandline_options *options, + const char *option); +#endif diff --git a/util/base64.c b/util/base64.c new file mode 100644 index 0000000..7f47cda --- /dev/null +++ b/util/base64.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * base64.c - RFC4648-compliant base64 encoding + * + * Copyright (c) 2020 Hannes Reinecke, SUSE + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> + +static const char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode() - base64-encode some bytes + * @src: the bytes to encode + * @srclen: number of bytes to encode + * @dst: (output) the base64-encoded string. Not NUL-terminated. + * + * Encodes the input string using characters from the set [A-Za-z0-9+,]. + * The encoded string is roughly 4/3 times the size of the input string. + * + * Return: length of the encoded string + */ +int base64_encode(const unsigned char *src, int srclen, char *dst) +{ + int i, bits = 0; + u_int32_t ac = 0; + char *cp = dst; + + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; + bits += 8; + do { + bits -= 6; + *cp++ = base64_table[(ac >> bits) & 0x3f]; + } while (bits >= 6); + } + if (bits) { + *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; + bits -= 6; + } + while (bits < 0) { + *cp++ = '='; + bits += 2; + } + + return cp - dst; +} + +/** + * base64_decode() - base64-decode some bytes + * @src: the base64-encoded string to decode + * @len: number of bytes to decode + * @dst: (output) the decoded bytes. + * + * Decodes the base64-encoded bytes @src according to RFC 4648. + * + * Return: number of decoded bytes + */ +int base64_decode(const char *src, int srclen, unsigned char *dst) +{ + u_int32_t ac = 0; + int i, bits = 0; + unsigned char *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64_table, src[i]); + + if (src[i] == '=') { + ac = (ac << 6); + bits += 6; + if (bits >= 8) + bits -= 8; + continue; + } + if (!p || !src[i]) + return -EINVAL; + ac = (ac << 6) | (p - base64_table); + bits += 6; + if (bits >= 8) { + bits -= 8; + *bp++ = (unsigned char)(ac >> bits); + } + } + if (ac && ((1 << bits) - 1)) + return -EAGAIN; + + return bp - dst; +} diff --git a/util/base64.h b/util/base64.h new file mode 100644 index 0000000..c0f62e2 --- /dev/null +++ b/util/base64.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _BASE64_H +#define _BASE64_H + +int base64_encode(const unsigned char *src, int len, char *dst); +int base64_decode(const char *src, int len, unsigned char *dst); + +#endif /* _BASE64_H */ diff --git a/util/cleanup.h b/util/cleanup.h new file mode 100644 index 0000000..ee9b120 --- /dev/null +++ b/util/cleanup.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __CLEANUP_H +#define __CLEANUP_H + +#include <unistd.h> +#include <stdlib.h> + +#include "util/mem.h" + +#define __cleanup__(fn) __attribute__((cleanup(fn))) + +#define DECLARE_CLEANUP_FUNC(name, type) \ + void name(type *__p) + +#define DEFINE_CLEANUP_FUNC(name, type, free_fn)\ +DECLARE_CLEANUP_FUNC(name, type) \ +{ \ + if (*__p) \ + free_fn(*__p); \ +} + +static inline void freep(void *p) +{ + free(*(void**) p); +} +#define _cleanup_free_ __cleanup__(freep) + +#define _cleanup_huge_ __cleanup__(nvme_free_huge) + +static inline void close_file(int *f) +{ + if (*f > STDERR_FILENO) + close(*f); +} +#define _cleanup_file_ __cleanup__(close_file) + +#endif diff --git a/util/crc32.c b/util/crc32.c new file mode 100644 index 0000000..bb5f129 --- /dev/null +++ b/util/crc32.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Copyright (C) 2002 Red Hat, Inc. + * This file is part of elfutils. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of either + * + * * the GNU Lesser General Public License as published by the Free + * Software Foundation; either version 3 of the License, or (at + * your option) any later version + * + * or + * + * * 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 + * + * or both in parallel, as here. + * + * elfutils 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 copies of the GNU General Public License and + * the GNU Lesser General Public License along with this program. If + * not, see <http://www.gnu.org/licenses/>. + */ + +/* https://sourceware.org/git/?p=elfutils.git;a=blob;f=lib/crc32.c;hb=575198c29a427392823cc8f2400579a23d06a875 */ + +#include "crc32.h" + +/* Table computed with Mark Adler's makecrc.c utility. */ +static const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d +}; + +uint32_t crc32(uint32_t crc, unsigned char *buf, size_t len) +{ + unsigned char *end; + + crc = ~crc; + for (end = buf + len; buf < end; ++buf) + crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); + return ~crc; +} diff --git a/util/crc32.h b/util/crc32.h new file mode 100644 index 0000000..e48c97d --- /dev/null +++ b/util/crc32.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef crc32_H +#define crc32_H + +#include <stdint.h> +#include <stddef.h> + +uint32_t crc32(uint32_t crc, unsigned char *buf, size_t len); + +#endif diff --git a/util/json.c b/util/json.c new file mode 100644 index 0000000..2de5848 --- /dev/null +++ b/util/json.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <stdio.h> +#include <errno.h> + +#include "json.h" +#include "types.h" + +struct json_object *util_json_object_new_double(long double d) +{ + struct json_object *obj; + char *str; + + if (asprintf(&str, "%Lf", d) < 0) + return NULL; + + obj = json_object_new_string(str); + + free(str); + return obj; + +} + +struct json_object *util_json_object_new_uint64(uint64_t i) +{ + struct json_object *obj; + char *str; + + if (asprintf(&str, "%" PRIu64, i) < 0) + return NULL; + + obj = json_object_new_string(str); + + free(str); + return obj; + +} + +static int util_json_object_string_to_number(struct json_object *jso, + struct printbuf *pb, int level, + int flags) +{ + ssize_t len = json_object_get_string_len(jso); + + printbuf_memappend(pb, json_object_get_string(jso), len); + + return 0; +} + +struct json_object *util_json_object_new_uint128(nvme_uint128_t val) +{ + struct json_object *obj; + + obj = json_object_new_string(uint128_t_to_string(val)); + json_object_set_serializer(obj, util_json_object_string_to_number, NULL, NULL); + + return obj; +} + +uint64_t util_json_object_get_uint64(struct json_object *obj) +{ + uint64_t val = 0; + + if (json_object_is_type(obj, json_type_string)) { + char *end = NULL; + const char *buf; + + buf = json_object_get_string(obj); + val = strtoull(buf, &end, 10); + if ((val == 0 && errno != 0) || (end == buf)) + return 0; + } + + return val; +} diff --git a/util/json.h b/util/json.h new file mode 100644 index 0000000..54e33e3 --- /dev/null +++ b/util/json.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef __JSON__H +#define __JSON__H + +#ifdef CONFIG_JSONC +#include <json.h> +#include "util/types.h" + +/* Wrappers around json-c's API */ + +#define json_create_object(o) json_object_new_object(o) +#define json_create_array(a) json_object_new_array(a) +#define json_free_object(o) json_object_put(o) +#define json_free_array(a) json_object_put(a) +#define json_object_add_value_uint(o, k, v) \ + json_object_object_add(o, k, json_object_new_uint64(v)) +#define json_object_add_value_int(o, k, v) \ + json_object_object_add(o, k, json_object_new_int(v)) +#ifndef CONFIG_JSONC_14 +#define json_object_new_uint64(v) util_json_object_new_uint64(v) +#define json_object_get_uint64(v) util_json_object_get_uint64(v) +#endif +#define json_object_add_value_uint64(o, k, v) \ + json_object_object_add(o, k, json_object_new_uint64(v)) +#define json_object_add_value_uint128(o, k, v) \ + json_object_object_add(o, k, util_json_object_new_uint128(v)) +#define json_object_add_value_double(o, k, v) \ + json_object_object_add(o, k, util_json_object_new_double(v)) +#define json_object_add_value_float(o, k, v) \ + json_object_object_add(o, k, json_object_new_double(v)) +static inline int json_object_add_value_string(struct json_object *o, const char *k, const char *v) { + return json_object_object_add(o, k, v ? json_object_new_string(v) : NULL); +} +#define json_object_add_value_array(o, k, v) \ + json_object_object_add(o, k, v) +#define json_object_add_value_object(o, k, v) \ + json_object_object_add(o, k, v) +#define json_array_add_value_object(o, k) \ + json_object_array_add(o, k) +static inline int json_array_add_value_string(struct json_object *o, const char *v) { + return json_object_array_add(o, v ? json_object_new_string(v) : NULL); +} +#define json_print_object(o, u) \ + printf("%s", json_object_to_json_string_ext(o, \ + JSON_C_TO_STRING_PRETTY | \ + JSON_C_TO_STRING_NOSLASHESCAPE)) + +struct json_object *util_json_object_new_double(long double d); +struct json_object *util_json_object_new_uint64(uint64_t i); +struct json_object *util_json_object_new_uint128(nvme_uint128_t val); +struct json_object *util_json_object_new_uint128(nvme_uint128_t val); + +uint64_t util_json_object_get_uint64(struct json_object *obj); + +#else /* !CONFIG_JSONC */ + +struct json_object; + +#endif + +#endif diff --git a/util/mem.c b/util/mem.c new file mode 100644 index 0000000..d2be46e --- /dev/null +++ b/util/mem.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include <stdlib.h> +#include <unistd.h> +#include <malloc.h> +#include <string.h> +#include <sys/mman.h> + +#include "mem.h" + +#include "common.h" + +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) +#define HUGE_MIN 0x80000 + +void *nvme_alloc(size_t len) +{ + void *p; + + len = ROUND_UP(len, 0x1000); + if (posix_memalign((void *)&p, getpagesize(), len)) + return NULL; + + memset(p, 0, len); + return p; +} + +void *nvme_realloc(void *p, size_t len) +{ + size_t old_len = malloc_usable_size(p); + + void *result = nvme_alloc(len); + + if (p) { + memcpy(result, p, min(old_len, len)); + free(p); + } + + return result; +} + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh) +{ + memset(mh, 0, sizeof(*mh)); + + len = ROUND_UP(len, 0x1000); + + /* + * For smaller allocation we just use posix_memalign and hope the kernel + * is able to convert to a contiguous memory region. + */ + if (len < HUGE_MIN) { + mh->p = nvme_alloc(len); + if (!mh->p) + return NULL; + mh->posix_memalign = true; + mh->len = len; + return mh->p; + } + + /* + * Larger allocation will almost certainly fail with the small + * allocation approach. Instead try pre-allocating memory from the + * HugeTLB pool. + * + * https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt + */ + mh->p = mmap(NULL, len, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0); + if (mh->p != MAP_FAILED) { + mh->len = len; + return mh->p; + } + + /* + * And if mmap fails because the pool is empty, try to use + * posix_memalign/madvise as fallback with a 2MB aligmnent in order to + * fullfil the request. This gives the kernel a chance to try to claim + * some huge pages. This might still fail though. + */ + len = ROUND_UP(len, 0x200000); + if (posix_memalign(&mh->p, 0x200000, len)) + return NULL; + mh->posix_memalign = true; + mh->len = len; + + memset(mh->p, 0, mh->len); + + if (madvise(mh->p, mh->len, MADV_HUGEPAGE) < 0) { + nvme_free_huge(mh); + return NULL; + } + + return mh->p; +} + +void nvme_free_huge(struct nvme_mem_huge *mh) + +{ + if (!mh || mh->len == 0) + return; + + if (mh->posix_memalign) + free(mh->p); + else + munmap(mh->p, mh->len); + + mh->len = 0; + mh->p = NULL; +} diff --git a/util/mem.h b/util/mem.h new file mode 100644 index 0000000..d13eb3a --- /dev/null +++ b/util/mem.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MEM_H_ +#define MEM_H_ + +#include <stddef.h> +#include <stdbool.h> + +void *nvme_alloc(size_t len); +void *nvme_realloc(void *p, size_t len); + +struct nvme_mem_huge { + size_t len; + bool posix_memalign; /* p has been allocated using posix_memalign */ + void *p; +}; + +void *nvme_alloc_huge(size_t len, struct nvme_mem_huge *mh); +void nvme_free_huge(struct nvme_mem_huge *mh); + +#endif /* MEM_H_ */ diff --git a/util/meson.build b/util/meson.build new file mode 100644 index 0000000..dfc683b --- /dev/null +++ b/util/meson.build @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +sources += [ + 'util/argconfig.c', + 'util/base64.c', + 'util/crc32.c', + 'util/mem.c', + 'util/suffix.c', + 'util/types.c', +] + +if json_c_dep.found() + sources += [ + 'util/json.c', + ] +endif diff --git a/util/suffix.c b/util/suffix.c new file mode 100644 index 0000000..f010f3b --- /dev/null +++ b/util/suffix.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2014 PMC-Sierra, 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. + * + */ + +/* + * + * Author: Logan Gunthorpe + * + * Date: Oct 23 2014 + * + * Description: + * Functions for dealing with number suffixes + * + */ + +#include "suffix.h" +#include "common.h" + +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <float.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> + +static struct si_suffix { + long double magnitude; + unsigned int exponent; + const char *suffix; +} si_suffixes[] = { + {1e30, 30, "Q"}, + {1e27, 27, "R"}, + {1e24, 24, "Y"}, + {1e21, 21, "Z"}, + {1e18, 18, "E"}, + {1e15, 15, "P"}, + {1e12, 12, "T"}, + {1e9, 9, "G"}, + {1e6, 6, "M"}, + {1e3, 3, "k"}, +}; + +const char *suffix_si_get(double *value) +{ + long double value_ld = *value; + const char *suffix = suffix_si_get_ld(&value_ld); + + *value = value_ld; + + return suffix; +} + +static bool suffix_si_check(const char val) +{ + int i; + struct si_suffix *s; + + for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) { + s = &si_suffixes[i]; + + if (val == *s->suffix) + return true; + } + + return false; +} + +int suffix_si_parse(const char *str, char **endptr, uint64_t *val) +{ + unsigned long long num, frac = 0; + char *sep, *tmp; + int frac_len = 0, len, i; + + num = strtoull(str, endptr, 0); + if (str == *endptr || + ((num == ULLONG_MAX) && errno == ERANGE)) + return -EINVAL; + + /* simple number, no decimal point not suffix */ + if ((*endptr)[0] == '\0') { + *val = num; + return 0; + } + + /* get rid of the decimal point */ + sep = localeconv()->decimal_point; + if (sep) + len = strlen(sep); + else + len = 0; + + for (i = 0; i < len; i++) { + if (suffix_si_check((*endptr)[i])) + break; + if (((*endptr)[i] == '\0') || (*endptr)[i] != sep[i]) + return -EINVAL; + } + + if (suffix_si_check((*endptr)[i])) { + if ((*endptr)[i + 1] != '\0') + return -EINVAL; + } else { + *endptr += len; + tmp = *endptr; + + /* extract the digits after decimal point */ + frac = strtoull(tmp, endptr, 0); + if (tmp == *endptr || + ((frac == ULLONG_MAX) && errno == ERANGE)) + return -EINVAL; + + /* test that we have max one character as suffix */ + if ((*endptr)[0] != '\0' && (*endptr)[1] != '\0') + return -EINVAL; + + frac_len = *endptr - tmp; + } + + for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) { + struct si_suffix *s = &si_suffixes[i]; + + if ((*endptr)[0] != s->suffix[0]) + continue; + + /* we should check for overflow */ + for (int j = 0; j < s->exponent; j++) + num *= 10; + + if (s->exponent > frac_len) { + for (int j = 0; j < s->exponent - frac_len; j++) + frac *= 10; + } else if (s->exponent < frac_len) { + for (int j = 0; j < frac_len - s->exponent; j++) + frac /= 10; + } else { + frac = 0; + } + + *val = num + frac; + return 0; + } + + if ((*endptr)[0] != '\0') + return -EINVAL; + + *val = num; + return 0; +} + +const char *suffix_si_get_ld(long double *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(si_suffixes); i++) { + struct si_suffix *s = &si_suffixes[i]; + + if (*value >= s->magnitude) { + *value /= s->magnitude; + return s->suffix; + } + } + + return ""; +} + +static struct binary_suffix { + int shift; + const char *suffix; +} binary_suffixes[] = { + {50, "Pi"}, + {40, "Ti"}, + {30, "Gi"}, + {20, "Mi"}, + {10, "Ki"}, +}; + +const char *suffix_binary_get(long long *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(binary_suffixes); i++) { + struct binary_suffix *s = &binary_suffixes[i]; + + if (llabs(*value) >= (1LL << s->shift)) { + *value = + (*value + (1LL << (s->shift - 1))) / (1LL << s->shift); + return s->suffix; + } + } + + return ""; +} + +const char *suffix_dbinary_get(double *value) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(binary_suffixes); i++) { + struct binary_suffix *s = &binary_suffixes[i]; + + if (fabs(*value) >= (1LL << s->shift)) { + *value = *value / (1LL << s->shift); + return s->suffix; + } + } + + return ""; +} + +int suffix_binary_parse(const char *str, char **endptr, uint64_t *val) +{ + uint64_t ret; + int i; + + ret = strtoull(str, endptr, 0); + if (str == *endptr || + ((ret == ULLONG_MAX) && errno == ERANGE)) + return -EINVAL; + + if (str == *endptr) { + *val = ret; + return 0; + } + + /* simple number, no decimal point, no suffix */ + if ((*endptr)[0] == '\0') { + *val = ret; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(binary_suffixes); i++) { + struct binary_suffix *s = &binary_suffixes[i]; + + if (tolower((*endptr)[0]) == tolower(s->suffix[0]) && + (s->suffix[0] != '\0' && + (((*endptr)[0] != '\0' && + (*endptr)[1] != '\0' && + (*endptr)[2] == '\0') && + (tolower((*endptr)[1]) == tolower(s->suffix[1]))))) { + ret <<= s->shift; + *val = ret; + return 0; + } + } + + return -EINVAL; +} diff --git a/util/suffix.h b/util/suffix.h new file mode 100644 index 0000000..5ea58f4 --- /dev/null +++ b/util/suffix.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * + * Copyright 2014 PMC-Sierra, 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. + * + */ + +/* + * + * Author: Logan Gunthorpe + * + * Date: Oct 23 2014 + * + * Description: + * Functions for dealing with number suffixes + * + */ + +#ifndef __ARGCONFIG_SUFFIX_H__ + +#include <inttypes.h> +#include <stdbool.h> + +const char *suffix_si_get(double *value); +int suffix_si_parse(const char *str, char **endptr, uint64_t *val); +const char *suffix_si_get_ld(long double *value); +const char *suffix_binary_get(long long *value); +const char *suffix_dbinary_get(double *value); +int suffix_binary_parse(const char *str, char **endptr, uint64_t *val); + +#endif diff --git a/util/types.c b/util/types.c new file mode 100644 index 0000000..376c734 --- /dev/null +++ b/util/types.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <locale.h> +#include <time.h> + +#include <ccan/endian/endian.h> + +#include "types.h" +#include "util/suffix.h" + +nvme_uint128_t le128_to_cpu(__u8 *data) +{ + nvme_uint128_t u; + nvme_uint128_t tmp; + + memcpy(tmp.bytes, data, 16); + u.words[0] = le32_to_cpu(tmp.words[3]); + u.words[1] = le32_to_cpu(tmp.words[2]); + u.words[2] = le32_to_cpu(tmp.words[1]); + u.words[3] = le32_to_cpu(tmp.words[0]); + return u; +} + +long double int128_to_double(__u8 *data) +{ + long double result = 0; + int i; + + for (i = 0; i < 16; i++) { + result *= 256; + result += data[15 - i]; + } + return result; +} + +uint64_t int48_to_long(__u8 *data) +{ + int i; + uint64_t result = 0; + + for (i = 0; i < 6; i++) { + result *= 256; + result += data[5 - i]; + } + return result; +} + +static long double uint128_t_to_double(nvme_uint128_t data) +{ + long double result = 0; + int i; + + for (i = 0; i < sizeof(data.words) / sizeof(*data.words); i++) { + result *= 4294967296; + result += data.words[i]; + } + + return result; +} + +static char *__uint128_t_to_string(nvme_uint128_t val, bool l10n) +{ + static char str[60]; + int idx = 60; + __u64 div, rem; + char *sep = NULL; + int i, len = 0, cl = 0; + + if (l10n) { + sep = localeconv()->thousands_sep; + len = strlen(sep); + cl = 1; + } + + /* terminate at the end, and build up from the ones */ + str[--idx] = '\0'; + + do { + if (len && !((sizeof(str) - idx) % (3 + cl))) { + for (i = 0; i < len; i++) + str[--idx] = sep[len - i - 1]; + } + + rem = val.words[0]; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[1]; + val.words[0] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[2]; + val.words[1] = div; + + div = rem / 10; + rem = ((rem - div * 10) << 32) + val.words[3]; + val.words[2] = div; + + div = rem / 10; + rem = rem - div * 10; + val.words[3] = div; + + str[--idx] = '0' + rem; + } while (val.words[0] || val.words[1] || val.words[2] || val.words[3]); + + return str + idx; +} + +char *uint128_t_to_string(nvme_uint128_t val) +{ + return __uint128_t_to_string(val, false); +} + +char *uint128_t_to_l10n_string(nvme_uint128_t val) +{ + return __uint128_t_to_string(val, true); +} + +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit) +{ + long double bytes = uint128_t_to_double(val) * bytes_per_unit; + static char str[40]; + const char *suffix = suffix_si_get_ld(&bytes); + int n = snprintf(str, sizeof(str), "%.2Lf %sB", bytes, suffix); + + if (n <= 0) + return ""; + + if (n >= sizeof(str)) + str[sizeof(str) - 1] = '\0'; + + return str; +} + +const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]) +{ + static char uuid_str[NVME_UUID_LEN_STRING]; + + nvme_uuid_to_string(uuid, uuid_str); + + return uuid_str; +} + +const char *util_fw_to_string(char *c) +{ + static char ret[9]; + int i; + + for (i = 0; i < 8; i++) + ret[i] = c[i] >= '!' && c[i] <= '~' ? c[i] : '.'; + ret[i] = '\0'; + return ret; +} + +int convert_ts(time_t time, char *ts_buf) +{ + struct tm time_info; + time_t time_human, time_ms; + char buf[80]; + + time_human = time / 1000; + time_ms = time % 1000; + + gmtime_r((const time_t *)&time_human, &time_info); + + strftime(buf, sizeof(buf), "%Y-%m-%dD|%H:%M:%S", &time_info); + sprintf(ts_buf, "%s:%03ld", buf, time_ms); + + return 0; +} + +void util_spinner(const char *disp_name, float percent) +{ + static const char dash[51] = {[0 ... 49] = '=', '\0'}; + static const char space[51] = {[0 ... 49] = ' ', '\0'}; + static const char spin[] = {'-', '\\', '|', '/' }; + static int progress; + static int i; + const char *dn = disp_name ? disp_name : ""; + + if (percent < 0) + percent = 0; + else if (percent > 1) + percent = 1; + + progress = (int)(percent * 100.0); + if (progress < 2) + printf("\r%s [%c%.*s] %3d%%", dn, + spin[i % 4], 49, + space, progress); + else if (progress < 100) + printf("\r%s [%.*s%c%.*s] %3d%%", dn, + progress / 2 - 1, dash, + spin[i % 4], 50 - progress / 2, + space, progress); + else + printf("\r%s [%.*s] %3d%%\n", dn, + 50, dash, 100); + i++; + + fflush(stdout); +} diff --git a/util/types.h b/util/types.h new file mode 100644 index 0000000..595958b --- /dev/null +++ b/util/types.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _MISC_H +#define _MISC_H + +/* type conversion helpers */ + +#include <stdint.h> +#include <linux/types.h> + +#include <libnvme.h> + +#define ABSOLUTE_ZERO_CELSIUS -273 + +static inline long kelvin_to_celsius(long t) +{ + return t + ABSOLUTE_ZERO_CELSIUS; +} + +/* uint128_t is not always available, define our own. */ +union nvme_uint128 { + __u8 bytes[16]; + __u32 words[4]; /* [0] is most significant word */ +}; + +typedef union nvme_uint128 nvme_uint128_t; + +nvme_uint128_t le128_to_cpu(__u8 *data); +long double int128_to_double(__u8 *data); +uint64_t int48_to_long(__u8 *data); + +char *uint128_t_to_string(nvme_uint128_t val); +char *uint128_t_to_l10n_string(nvme_uint128_t val); +char *uint128_t_to_si_string(nvme_uint128_t val, __u32 bytes_per_unit); +const char *util_uuid_to_string(unsigned char uuid[NVME_UUID_LEN]); +const char *util_fw_to_string(char *c); + +/** + * @brief convert time_t format time to a human readable string + * + * @param time, input time_t time + * @param ts_buf, output time string + * @Note, time string format is "Y-M-D|H:M:S:MS" + * + * @return 0 success + */ +int convert_ts(time_t time, char *ts_buf); + +/** + * @brief print once a progress of spinner to stdout + * the output will be looks like if disp_name is "LogDump" and percent is 0.5 + * LogDump [========================- ] 50% + + * + * @param disp_name, const string displayed before spiner + * @param percent [0, 1.0] about the progress + * + */ +void util_spinner(const char *disp_name, float percent); + +#endif /* _MISC_H */ |