Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_hex.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
26
27#include "nilorea/n_common.h"
28#include "nilorea/n_hex.h"
29#include "nilorea/n_log.h"
30#include "nilorea/n_str.h"
31
32#include <stdio.h>
33#include <string.h>
34
35static int failures = 0;
36
37/* Parse the -V LOG_LEVEL verbosity argument. */
38void process_args(int argc, char** argv) {
39 int opt = 0;
40 while ((opt = getopt(argc, argv, "hvV:")) != EOF) {
41 switch (opt) {
42 case 'V':
43 if (!strncmp("LOG_NULL", optarg, 8))
45 else if (!strncmp("LOG_NOTICE", optarg, 10))
47 else if (!strncmp("LOG_INFO", optarg, 8))
49 else if (!strncmp("LOG_ERR", optarg, 7))
51 else if (!strncmp("LOG_DEBUG", optarg, 9))
53 else {
54 fprintf(stderr, "Unknown log level %s\n", optarg);
55 exit(1);
56 }
57 break;
58 case 'v':
59 fprintf(stderr, "ex_hex\n");
60 exit(1);
61 case 'h':
62 default:
63 fprintf(stderr, "usage: %s [-V LOG_LEVEL]\n", argv[0]);
64 break;
65 }
66 }
67}
68
69static void expect_encode(const unsigned char* data, size_t len, const char* want) {
70 N_STR* out = n_hex_encode(data, len);
71 if (!out) {
72 n_log(LOG_ERR, "n_hex_encode returned NULL for expected '%s'", want);
73 failures++;
74 return;
75 }
76 if (out->written != strlen(want) || strcmp(out->data, want) != 0) {
77 n_log(LOG_ERR, "n_hex_encode: got '%s' (written %zu) expected '%s'", out->data, out->written, want);
78 failures++;
79 }
80 free_nstr(&out);
81}
82
83static void test_encode(void) {
84 const unsigned char a[] = {0x00, 0x01, 0xff, 0xab};
85 expect_encode(a, sizeof(a), "0001ffab");
86
87 const unsigned char b[] = {0xDE, 0xAD, 0xBE, 0xEF};
88 expect_encode(b, sizeof(b), "deadbeef");
89
90 /* zero length yields an empty string */
91 expect_encode((const unsigned char*)"", 0, "");
92 /* NULL data with len 0 is allowed and yields an empty string */
93 expect_encode(NULL, 0, "");
94}
95
96static void test_decode_roundtrip(void) {
97 size_t n = 123;
98 unsigned char* buf = n_hex_decode("0001ffab", &n);
99 if (!buf || n != 4) {
100 n_log(LOG_ERR, "decode '0001ffab': got len %zu expected 4", n);
101 failures++;
102 } else if (buf[0] != 0x00 || buf[1] != 0x01 || buf[2] != 0xff || buf[3] != 0xab) {
103 n_log(LOG_ERR, "decode '0001ffab': wrong bytes");
104 failures++;
105 }
106 Free(buf);
107
108 /* uppercase and whitespace are tolerated and produce the same bytes */
109 n = 0;
110 buf = n_hex_decode(" DE AD\tBE\nEF ", &n);
111 if (!buf || n != 4 || buf[0] != 0xde || buf[1] != 0xad || buf[2] != 0xbe || buf[3] != 0xef) {
112 n_log(LOG_ERR, "decode with whitespace/upper failed (len %zu)", n);
113 failures++;
114 }
115 Free(buf);
116
117 /* round-trip a buffer containing an embedded NUL */
118 const unsigned char payload[] = {0x00, 0x41, 0x00, 0x42};
119 N_STR* enc = n_hex_encode(payload, sizeof(payload));
120 if (!enc || strcmp(enc->data, "00410042") != 0) {
121 n_log(LOG_ERR, "encode with embedded NUL failed");
122 failures++;
123 } else {
124 n = 0;
125 unsigned char* dec = n_hex_decode(enc->data, &n);
126 if (!dec || n != sizeof(payload) || memcmp(dec, payload, sizeof(payload)) != 0) {
127 n_log(LOG_ERR, "round-trip with embedded NUL failed (len %zu)", n);
128 failures++;
129 }
130 Free(dec);
131 }
132 free_nstr(&enc);
133}
134
135static void test_decode_errors(void) {
136 size_t n = 7;
137 unsigned char* buf = n_hex_decode("abc", &n); /* odd digit count */
138 if (buf || n != 0) {
139 n_log(LOG_ERR, "decode odd-length should fail");
140 failures++;
141 Free(buf);
142 }
143 n = 7;
144 buf = n_hex_decode("0g", &n); /* invalid character */
145 if (buf || n != 0) {
146 n_log(LOG_ERR, "decode invalid char should fail");
147 failures++;
148 Free(buf);
149 }
150 /* empty input decodes to a zero-length, non-NULL buffer */
151 n = 7;
152 buf = n_hex_decode("", &n);
153 if (!buf || n != 0) {
154 n_log(LOG_ERR, "decode empty should yield len 0 buffer (len %zu)", n);
155 failures++;
156 }
157 Free(buf);
158 /* NULL input is rejected */
159 if (n_hex_decode(NULL, &n) != NULL) {
160 n_log(LOG_ERR, "decode NULL should fail");
161 failures++;
162 }
163}
164
165/* Zero-allocation, fixed-length caller-buffer variants. */
166static void test_bounded(void) {
167 const unsigned char a[] = {0x00, 0x01, 0xff, 0xab};
168 char buf[9];
169 size_t w = n_hex_encode_to(buf, sizeof(buf), a, sizeof(a));
170 if (w != 8 || strcmp(buf, "0001ffab") != 0) {
171 n_log(LOG_ERR, "n_hex_encode_to: got '%s' (w %zu) expected '0001ffab'", buf, w);
172 failures++;
173 }
174 /* a buffer smaller than 2*len+1 is rejected */
175 if (n_hex_encode_to(buf, 8, a, sizeof(a)) != 0) {
176 n_log(LOG_ERR, "n_hex_encode_to: undersized buffer should return 0");
177 failures++;
178 }
179
180 unsigned char out[4];
181 if (n_hex_decode_to(out, sizeof(out), "DEADbeef", 4) != TRUE || out[0] != 0xde || out[1] != 0xad || out[2] != 0xbe || out[3] != 0xef) {
182 n_log(LOG_ERR, "n_hex_decode_to: DEADbeef mismatch");
183 failures++;
184 }
185 /* a hex string shorter than 2*nbytes fails without over-reading */
186 if (n_hex_decode_to(out, sizeof(out), "dead", 4) != FALSE) {
187 n_log(LOG_ERR, "n_hex_decode_to: short input should fail");
188 failures++;
189 }
190 /* an invalid hex digit fails */
191 if (n_hex_decode_to(out, sizeof(out), "deadbeeg", 4) != FALSE) {
192 n_log(LOG_ERR, "n_hex_decode_to: invalid digit should fail");
193 failures++;
194 }
195 /* a too-small output buffer is rejected */
196 if (n_hex_decode_to(out, 3, "deadbeef", 4) != FALSE) {
197 n_log(LOG_ERR, "n_hex_decode_to: undersized out should fail");
198 failures++;
199 }
200 /* encode_to -> decode_to round-trip */
201 unsigned char rt[4];
202 char hb[9];
203 n_hex_encode_to(hb, sizeof(hb), a, sizeof(a));
204 if (n_hex_decode_to(rt, sizeof(rt), hb, sizeof(a)) != TRUE || memcmp(rt, a, sizeof(a)) != 0) {
205 n_log(LOG_ERR, "n_hex round-trip (to) failed");
206 failures++;
207 }
208}
209
210int main(int argc, char** argv) {
212 process_args(argc, argv);
213
214 test_encode();
217 test_bounded();
218
219 if (failures) {
220 n_log(LOG_ERR, "ex_hex: %d failure(s)", failures);
221 return 1;
222 }
223 n_log(LOG_NOTICE, "ex_hex: all checks passed");
224 return 0;
225}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void test_encode(void)
Definition ex_hex.c:83
static void expect_encode(const unsigned char *data, size_t len, const char *want)
Definition ex_hex.c:69
static void test_bounded(void)
Definition ex_hex.c:166
static void test_decode_errors(void)
Definition ex_hex.c:135
static void test_decode_roundtrip(void)
Definition ex_hex.c:96
#define Free(__ptr)
Free Handler to get errors.
Definition n_common.h:263
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_NULL
no log output
Definition n_log.h:70
#define LOG_INFO
informational
Definition n_log.h:106
unsigned char * n_hex_decode(const char *hex, size_t *out_len)
decode a hex string (ASCII whitespace between digits is ignored) into a freshly allocated,...
Definition n_hex.c:71
size_t n_hex_encode_to(char *out, size_t out_size, const unsigned char *data, size_t len)
encode len bytes of data as lowercase hex into the caller-provided buffer out (needs out_size >= 2*le...
Definition n_hex.c:117
int n_hex_decode_to(unsigned char *out, size_t out_size, const char *hex, size_t nbytes)
decode exactly nbytes bytes from the first 2*nbytes hex digits of hex (case-insensitive,...
Definition n_hex.c:141
N_STR * n_hex_encode(const unsigned char *data, size_t len)
encode len bytes of data as a lowercase hex N_STR (2*len characters); free with free_nstr.
Definition n_hex.c:48
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
Definition n_str.h:68
char * data
the string
Definition n_str.h:63
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
A box including a string and his lenght.
Definition n_str.h:61
Common headers and low-level functions & define.
Hexadecimal encode/decode helpers.
Generic log system.
N_STR and string function declaration.