Skip to content

Commit d01e7f6

Browse files
Integer encoding utilities.
This adds function for reading little-endian i32, u32, i64, and u64 from pointers to memory. This allows us to decode integers in a single line instead of doing a declare+memcpy+byteswap that clutters the code and prevents us from using `const` and correct signedness. Instead, we can declare and initialize integers of the exact size and sign that we want in a single line.
1 parent 0ba76cf commit d01e7f6

File tree

2 files changed

+190
-1
lines changed

2 files changed

+190
-1
lines changed

src/common/src/mlib/intencode.h

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/**
2+
* @file mlib/intencode.h
3+
* @brief Integer encoding functions
4+
* @date 2025-01-31
5+
*
6+
* @copyright Copyright (c) 2025
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License");
9+
* you may not use this file except in compliance with the License.
10+
* You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing, software
15+
* distributed under the License is distributed on an "AS IS" BASIS,
16+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* See the License for the specific language governing permissions and
18+
* limitations under the License.
19+
*/
20+
#pragma once
21+
22+
#include <mlib/config.h>
23+
#include <mlib/loop.h>
24+
25+
#include <stdint.h>
26+
#include <string.h>
27+
28+
/**
29+
* @brief Decode an unsigned 32-bit little-endian integer from a memory buffer
30+
*/
31+
static inline uint32_t
32+
mlib_read_u32le (const void *buf)
33+
{
34+
uint32_t ret = 0;
35+
if (mlib_is_little_endian ()) {
36+
// Optimize: The platform uses a LE encoding already
37+
memcpy (&ret, buf, sizeof ret);
38+
} else {
39+
// Portable decode of an LE integer
40+
const uint8_t *cptr = (const uint8_t *) buf;
41+
mlib_foreach_urange (i, sizeof ret) {
42+
ret <<= 8;
43+
ret |= cptr[i];
44+
}
45+
}
46+
return ret;
47+
}
48+
49+
/**
50+
* @brief Decode an signed 32-bit little-endian integer from a memory buffer
51+
*/
52+
static inline int32_t
53+
mlib_read_i32le (const void *buf)
54+
{
55+
return (int32_t) mlib_read_u32le (buf);
56+
}
57+
58+
/**
59+
* @brief Decode an unsigned 64-bit little-endian integer from a memory buffer
60+
*/
61+
static inline uint64_t
62+
mlib_read_u64le (const void *buf)
63+
{
64+
uint64_t ret = 0;
65+
if (mlib_is_little_endian ()) {
66+
// Optimize: The platform uses a LE encoding already
67+
memcpy (&ret, buf, sizeof ret);
68+
} else {
69+
// Portable decode of an LE integer
70+
const uint8_t *cptr = (const uint8_t *) buf;
71+
mlib_foreach_urange (i, sizeof ret) {
72+
ret <<= 8;
73+
ret |= cptr[i];
74+
}
75+
}
76+
return ret;
77+
}
78+
79+
/**
80+
* @brief Decode an signed 64-bit little-endian integer from a memory buffer
81+
*/
82+
static inline int64_t
83+
mlib_read_i64le (const void *buf)
84+
{
85+
return (int64_t) mlib_read_u64le (buf);
86+
}
87+
88+
/**
89+
* @brief Write an unsigned 32-bit little-endian integer into a destination
90+
*
91+
* @return void* The address after the written value
92+
*/
93+
static inline void *
94+
mlib_write_u32le (void *out, const uint32_t value)
95+
{
96+
uint8_t *o = (uint8_t *) out;
97+
if (mlib_is_little_endian ()) {
98+
memcpy (o, &value, sizeof value);
99+
return o + sizeof value;
100+
}
101+
mlib_foreach_urange (i, sizeof value) {
102+
*o++ = (value >> (8u * i)) & 0xffu;
103+
}
104+
return o;
105+
}
106+
107+
/**
108+
* @brief Write a signed 32-bit little-endian integer into a destination
109+
*
110+
* @return void* The address after the written value
111+
*/
112+
static inline void *
113+
mlib_write_i32le (void *out, int32_t value)
114+
{
115+
return mlib_write_u32le (out, (uint32_t) value);
116+
}
117+
118+
/**
119+
* @brief Write an unsigned 64-bit little-endian integer into a destination
120+
*
121+
* @return void* The address after the written value
122+
*/
123+
static inline void *
124+
mlib_write_u64le (void *out, const uint64_t value)
125+
{
126+
uint8_t *o = (uint8_t *) out;
127+
if (mlib_is_little_endian ()) {
128+
memcpy (o, &value, sizeof value);
129+
return o + sizeof value;
130+
}
131+
mlib_foreach_urange (i, sizeof value) {
132+
*o++ = (value >> (8u * i)) & 0xffu;
133+
}
134+
return o;
135+
}
136+
137+
/**
138+
* @brief Write an signed 64-bit little-endian integer into a destination
139+
*
140+
* @return void* The address after the written value
141+
*/
142+
static inline void *
143+
mlib_write_i64le (void *out, int64_t value)
144+
{
145+
return mlib_write_u64le (out, (uint64_t) value);
146+
}
147+
148+
/**
149+
* @brief Write a little-endian 64-bit floating point (double) to the given
150+
* memory location
151+
*
152+
* @return void* The address after the written value.
153+
*/
154+
static inline void *
155+
mlib_write_f64le (void *out, double d)
156+
{
157+
mlib_static_assert (sizeof (double) == sizeof (uint64_t));
158+
uint64_t bits;
159+
memcpy (&bits, &d, sizeof d);
160+
return mlib_write_u64le (out, bits);
161+
}

src/common/tests/test-mlib.c

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <mlib/intutil.h>
44
#include <mlib/config.h>
5+
#include <mlib/intencode.h>
56
#include <mlib/loop.h>
67
#include <mlib/cmp.h>
78
#include <mlib/test.h>
@@ -401,6 +402,31 @@ _test_assert_aborts (void)
401402
ASSERT (a == 0);
402403
}
403404

405+
void
406+
_test_int_encoding (void)
407+
{
408+
{
409+
const char *buf = "\x01\x02\x03\x04";
410+
const uint32_t val = mlib_read_u32le (buf);
411+
mlib_check (val, eq, 0x04030201);
412+
}
413+
414+
{
415+
char buf[9] = {0};
416+
char *o = mlib_write_i32le (buf, 0x01020304);
417+
mlib_check (o, ptreq, buf + 4);
418+
mlib_check (buf, streq, "\x04\x03\x02\x01");
419+
420+
o = mlib_write_i32le (o, 42);
421+
mlib_check (o, ptreq, buf + 8);
422+
mlib_check (buf, streq, "\x04\x03\x02\x01*");
423+
424+
o = mlib_write_i64le (buf, 0x0102030405060708);
425+
mlib_check (o, ptreq, buf + 8);
426+
mlib_check (buf, streq, "\x08\x07\x06\x05\x04\x03\x02\x01");
427+
}
428+
}
429+
404430
void
405431
_test_foreach (void)
406432
{
@@ -446,7 +472,8 @@ _test_foreach (void)
446472
int arr[] = {1, 2, 3};
447473
int sum = 0;
448474
n_loops = 0;
449-
mlib_foreach_arr (int, n, arr) {
475+
mlib_foreach_arr (int, n, arr)
476+
{
450477
n_loops++;
451478
sum += *n;
452479
}
@@ -463,6 +490,7 @@ test_mlib_install (TestSuite *suite)
463490
TestSuite_Add (suite, "/mlib/cmp", _test_cmp);
464491
TestSuite_Add (suite, "/mlib/in-range", _test_in_range);
465492
TestSuite_Add (suite, "/mlib/assert-aborts", _test_assert_aborts);
493+
TestSuite_Add (suite, "/mlib/int-encoding", _test_int_encoding);
466494
TestSuite_Add (suite, "/mlib/foreach", _test_foreach);
467495
}
468496

0 commit comments

Comments
 (0)