[libvirt] [PATCH 1/3] util: add virendian.h macros
John Ferlan
jferlan at redhat.com
Tue Feb 12 00:56:56 UTC 2013
On 02/11/2013 07:07 PM, Eric Blake wrote:
> We have several cases where we need to read endian-dependent
> data regardless of host endianness; rather than open-coding
> these call sites, it will be nicer to funnel things through
> a macro.
>
> The virendian.h file can be expanded to add writer functions,
> and/or 16-bit access patterns, if needed. Also, if we need
> to turn things into a function to avoid multiple evaluations
> of buf, that can be done later. But for now, a macro worked.
>
> * src/util/virendian.h: New file.
> * src/Makefile.am (UTIL_SOURCES): Ship it.
> * tests/virendiantest.c: New test.
> * tests/Makefile.am (test_programs, virendiantest_SOURCES): Run
> the test.
> * .gitignore: Ignore built file.
> ---
> .gitignore | 1 +
> src/Makefile.am | 1 +
> src/util/virendian.h | 93 +++++++++++++++++++++++++++++++++++++++++++++
> tests/Makefile.am | 8 +++-
> tests/virendiantest.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 203 insertions(+), 2 deletions(-)
> create mode 100644 src/util/virendian.h
> create mode 100644 tests/virendiantest.c
>
> diff --git a/.gitignore b/.gitignore
> index 1670637..8afbf33 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -177,6 +177,7 @@
> /tests/virbitmaptest
> /tests/virbuftest
> /tests/virdrivermoduletest
> +/tests/virendiantest
> /tests/virhashtest
> /tests/virkeyfiletest
> /tests/virlockspacetest
> diff --git a/src/Makefile.am b/src/Makefile.am
> index f6162df..d554aa1 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -67,6 +67,7 @@ UTIL_SOURCES = \
> util/virdbus.c util/virdbus.h \
> util/virdnsmasq.c util/virdnsmasq.h \
> util/virebtables.c util/virebtables.h \
> + util/virendian.h \
> util/virerror.c util/virerror.h \
> util/virevent.c util/virevent.h \
> util/vireventpoll.c util/vireventpoll.h \
> diff --git a/src/util/virendian.h b/src/util/virendian.h
> new file mode 100644
> index 0000000..eefe48c
> --- /dev/null
> +++ b/src/util/virendian.h
> @@ -0,0 +1,93 @@
> +/*
> + * virendian.h: aid for reading endian-specific data
> + *
> + * Copyright (C) 2013 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#ifndef __VIR_ENDIAN_H__
> +# define __VIR_ENDIAN_H__
> +
> +# include "internal.h"
> +
> +/* The interfaces in this file are provided as macros for speed. */
> +
> +/**
> + * virReadBufInt64BE:
> + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*');
> + * evaluating buf must not have any side effects
> + *
> + * Read 8 bytes at BUF as a big-endian 64-bit number. Caller is
> + * responsible to avoid reading beyond array bounds.
> + */
> +# define virReadBufInt64BE(buf) \
> + (((uint64_t)(uint8_t)((buf)[0]) << 56) | \
> + ((uint64_t)(uint8_t)((buf)[1]) << 48) | \
> + ((uint64_t)(uint8_t)((buf)[2]) << 40) | \
> + ((uint64_t)(uint8_t)((buf)[3]) << 32) | \
> + ((uint64_t)(uint8_t)((buf)[4]) << 24) | \
> + ((uint64_t)(uint8_t)((buf)[5]) << 16) | \
> + ((uint64_t)(uint8_t)((buf)[6]) << 8) | \
> + (uint64_t)(uint8_t)((buf)[7]))
> +
> +/**
> + * virReadBufInt64LE:
> + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*');
> + * evaluating buf must not have any side effects
> + *
> + * Read 8 bytes at BUF as a little-endian 64-bit number. Caller is
> + * responsible to avoid reading beyond array bounds.
> + */
> +# define virReadBufInt64LE(buf) \
> + ((uint64_t)(uint8_t)((buf)[0]) | \
> + ((uint64_t)(uint8_t)((buf)[1]) << 8) | \
> + ((uint64_t)(uint8_t)((buf)[2]) << 16) | \
> + ((uint64_t)(uint8_t)((buf)[3]) << 24) | \
> + ((uint64_t)(uint8_t)((buf)[4]) << 32) | \
> + ((uint64_t)(uint8_t)((buf)[5]) << 40) | \
> + ((uint64_t)(uint8_t)((buf)[6]) << 48) | \
> + ((uint64_t)(uint8_t)((buf)[7]) << 56))
> +
> +/**
> + * virReadBufInt32BE:
> + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*');
> + * evaluating buf must not have any side effects
> + *
> + * Read 4 bytes at BUF as a big-endian 32-bit number. Caller is
> + * responsible to avoid reading beyond array bounds.
> + */
> +# define virReadBufInt32BE(buf) \
> + (((uint32_t)(uint8_t)((buf)[0]) << 24) | \
> + ((uint32_t)(uint8_t)((buf)[1]) << 16) | \
> + ((uint32_t)(uint8_t)((buf)[2]) << 8) | \
> + (uint32_t)(uint8_t)((buf)[3]))
> +
> +/**
> + * virReadBufInt32LE:
> + * @buf: byte to start reading at (can be 'char*' or 'unsigned char*');
> + * evaluating buf must not have any side effects
> + *
> + * Read 4 bytes at BUF as a little-endian 32-bit number. Caller is
> + * responsible to avoid reading beyond array bounds.
> + */
> +# define virReadBufInt32LE(buf) \
> + ((uint32_t)(uint8_t)((buf)[0]) | \
> + ((uint32_t)(uint8_t)((buf)[1]) << 8) | \
> + ((uint32_t)(uint8_t)((buf)[2]) << 16) | \
> + ((uint32_t)(uint8_t)((buf)[3]) << 24))
> +
> +#endif /* __VIR_ENDIAN_H__ */
Looking at byteswap.h.in I'm reminded of what I've seen in a previous job where
we had lots of byteswapping to do. The macros will "isolate" each byte to move, eg:
#define bswap_32(x) ((((x) & 0x000000FF) << 24) | \
(((x) & 0x0000FF00) << 8) | \
(((x) & 0x00FF0000) >> 8) | \
(((x) & 0xFF000000) >> 24))
Not sure if it's necessary in this case, but figured I'd ask.
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 0194db2..7d0a88e 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -1,6 +1,6 @@
> ## Process this file with automake to produce Makefile.in
>
> -## Copyright (C) 2005-2012 Red Hat, Inc.
> +## Copyright (C) 2005-2013 Red Hat, Inc.
> ## See COPYING.LIB for the License of this software
>
> SHELL = $(PREFERABLY_POSIX_SHELL)
> @@ -95,7 +95,7 @@ test_programs = virshtest sockettest \
> utiltest shunloadtest \
> virtimetest viruritest virkeyfiletest \
> virauthconfigtest \
> - virbitmaptest \
> + virbitmaptest virendiantest \
> virlockspacetest \
> virstringtest \
> virportallocatortest \
> @@ -649,6 +649,10 @@ virbitmaptest_SOURCES = \
> virbitmaptest.c testutils.h testutils.c
> virbitmaptest_LDADD = $(LDADDS)
>
> +virendiantest_SOURCES = \
> + virendiantest.c testutils.h testutils.c
> +virendiantest_LDADD = $(LDADDS)
> +
> jsontest_SOURCES = \
> jsontest.c testutils.h testutils.c
> jsontest_LDADD = $(LDADDS)
> diff --git a/tests/virendiantest.c b/tests/virendiantest.c
> new file mode 100644
> index 0000000..386ef33
> --- /dev/null
> +++ b/tests/virendiantest.c
> @@ -0,0 +1,102 @@
> +/*
> + * Copyright (C) 2013 Red Hat, Inc.
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library 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
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library. If not, see
> + * <http://www.gnu.org/licenses/>.
> + *
> + */
> +
> +#include <config.h>
> +
> +#include "testutils.h"
> +
> +#include "virendian.h"
> +
> +static int
> +test1(const void *data ATTRIBUTE_UNUSED)
> +{
> + /* Regular char should work, even if signed, and even with
> + * unaligned access. */
> + char array[] = { 1, 2, 3, 4, 5, 6, 7, 8,
> + 0x89, 0x8a, 0x8b, 0x8c, 0x8d };
> + int ret = -1;
> +
> + if (virReadBufInt64BE(array) != 0x0102030405060708ULL)
> + goto cleanup;
> + if (virReadBufInt64BE(array + 5) != 0x060708898a8b8c8dULL)
> + goto cleanup;
> + if (virReadBufInt64LE(array) != 0x0807060504030201ULL)
> + goto cleanup;
> + if (virReadBufInt64LE(array + 5) != 0x8d8c8b8a89080706ULL)
> + goto cleanup;
> +
> + if (virReadBufInt32BE(array) != 0x01020304U)
> + goto cleanup;
> + if (virReadBufInt32BE(array + 8) != 0x898a8b8cU)
> + goto cleanup;
> + if (virReadBufInt32LE(array) != 0x04030201U)
> + goto cleanup;
> + if (virReadBufInt32LE(array + 8) != 0x8c8b8a89U)
> + goto cleanup;
> +
> + ret = 0;
> +cleanup:
> + return ret;
> +}
> +
> +static int
> +test2(const void *data ATTRIBUTE_UNUSED)
> +{
> + /* Unsigned char should work without cast, even if unaligned access. */
> + unsigned char array[] = { 1, 2, 3, 4, 5, 6, 7, 8,
> + 0x89, 0x8a, 0x8b, 0x8c, 0x8d };
> + int ret = -1;
> +
> + if (virReadBufInt64BE(array) != 0x0102030405060708ULL)
> + goto cleanup;
> + if (virReadBufInt64BE(array + 5) != 0x060708898a8b8c8dULL)
> + goto cleanup;
> + if (virReadBufInt64LE(array) != 0x0807060504030201ULL)
> + goto cleanup;
> + if (virReadBufInt64LE(array + 5) != 0x8d8c8b8a89080706ULL)
> + goto cleanup;
> +
> + if (virReadBufInt32BE(array) != 0x01020304U)
> + goto cleanup;
> + if (virReadBufInt32BE(array + 8) != 0x898a8b8cU)
> + goto cleanup;
> + if (virReadBufInt32LE(array) != 0x04030201U)
> + goto cleanup;
> + if (virReadBufInt32LE(array + 8) != 0x8c8b8a89U)
> + goto cleanup;
> +
> + ret = 0;
> +cleanup:
> + return ret;
> +}
> +
> +static int
> +mymain(void)
> +{
> + int ret = 0;
> +
> + if (virtTestRun("test1", 1, test1, NULL) < 0)
> + ret = -1;
> + if (virtTestRun("test2", 1, test2, NULL) < 0)
> + ret = -1;
> +
> + return ret;
> +}
> +
> +VIRT_TEST_MAIN(mymain)
>
ACK
More information about the libvir-list
mailing list