Add header for access to potentially unaligned data.
authorBen Pfaff <blp@nicira.com>
Fri, 7 May 2010 21:31:04 +0000 (14:31 -0700)
committerBen Pfaff <blp@nicira.com>
Fri, 7 May 2010 21:36:07 +0000 (14:36 -0700)
I had been under the impression that "memcpy" was a valid way to copy
unaligned data into an aligned location for access.  But testing on SPARC
has shown that GCC doesn't always honor that intention.  It seems that, if
GCC can see that there is a pointer of a type that requires alignment to
a given object, then it will access it directly regardless of whether
memcpy() is used to copy it.

This commit adds a new header with functions to access unaligned data.  I
managed to come up with two techniques, one GCC-specific, one generic, that
do avoid the misaligned access in my test case.  The GCC-specific technique
is the same one used by the Linux kernel (although no code has been
literally copied).  The other one seemed obvious but possibly slow
depending on the compiler's ability to optimize.

The following commit adds a user.

lib/automake.mk
lib/unaligned.h [new file with mode: 0644]

index 6554b44a0bcb509c5a4b85bc49cd84add79c2fbf..56675384d46061ca9ddd6c887a775b3625512774 100644 (file)
@@ -134,6 +134,7 @@ lib_libopenvswitch_a_SOURCES = \
        lib/timeval.c \
        lib/timeval.h \
        lib/type-props.h \
+       lib/unaligned.h \
        lib/unicode.c \
        lib/unicode.h \
        lib/unixctl.c \
diff --git a/lib/unaligned.h b/lib/unaligned.h
new file mode 100644 (file)
index 0000000..fb167a4
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2010 Nicira Networks.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UNALIGNED_H
+#define UNALIGNED_H 1
+
+#include <stdint.h>
+#include "xtoxll.h"
+
+/* Public API. */
+static inline uint16_t get_unaligned_u16(const uint16_t *);
+static inline uint32_t get_unaligned_u32(const uint32_t *);
+static inline uint64_t get_unaligned_u64(const uint64_t *);
+static inline void put_unaligned_u16(uint16_t *, uint16_t);
+static inline void put_unaligned_u32(uint32_t *, uint32_t);
+static inline void put_unaligned_u64(uint64_t *, uint64_t);
+
+#ifdef __GNUC__
+/* GCC implementations. */
+#define GCC_UNALIGNED_ACCESSORS(SIZE)                       \
+struct unaligned_u##SIZE {                                  \
+    uint##SIZE##_t x __attribute__((__packed__));           \
+};                                                          \
+static inline struct unaligned_u##SIZE *                    \
+unaligned_u##SIZE(const uint##SIZE##_t *p)                  \
+{                                                           \
+    return (struct unaligned_u##SIZE *) p;                  \
+}                                                           \
+                                                            \
+static inline uint##SIZE##_t                                \
+get_unaligned_u##SIZE(const uint##SIZE##_t *p)              \
+{                                                           \
+    return unaligned_u##SIZE(p)->x;                         \
+}                                                           \
+                                                            \
+static inline void                                          \
+put_unaligned_u##SIZE(uint##SIZE##_t *p, uint##SIZE##_t x)  \
+{                                                           \
+    unaligned_u##SIZE(p)->x = x;                            \
+}
+
+GCC_UNALIGNED_ACCESSORS(16);
+GCC_UNALIGNED_ACCESSORS(32);
+GCC_UNALIGNED_ACCESSORS(64);
+#else
+/* Generic implementations. */
+
+static inline uint16_t get_unaligned_u16(const uint16_t *p_)
+{
+    const uint8_t *p = (const uint8_t *) p_;
+    return ntohs((p[0] << 8) | p[1]);
+}
+
+static inline void put_unaligned_u16(uint16_t *p_, uint16_t x_)
+{
+    uint8_t *p = (uint8_t *) p_;
+    uint16_t x = ntohs(x_);
+
+    p[0] = x >> 8;
+    p[1] = x;
+}
+
+static inline uint32_t get_unaligned_u32(const uint32_t *p_)
+{
+    const uint8_t *p = (const uint8_t *) p_;
+    return ntohl((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+}
+
+static inline void put_unaligned_u32(uint32_t *p_, uint32_t x_)
+{
+    uint8_t *p = (uint8_t *) p_;
+    uint32_t x = ntohl(x_);
+
+    p[0] = x >> 24;
+    p[1] = x >> 16;
+    p[2] = x >> 8;
+    p[3] = x;
+}
+
+static inline uint64_t get_unaligned_u64(const uint64_t *p_)
+{
+    const uint8_t *p = (const uint8_t *) p_;
+    return ntohll(((uint64_t) p[0] << 56)
+                  | ((uint64_t) p[1] << 48)
+                  | ((uint64_t) p[2] << 40)
+                  | ((uint64_t) p[3] << 32)
+                  | (p[4] << 24)
+                  | (p[5] << 16)
+                  | (p[6] << 8)
+                  | p[7]);
+}
+
+static inline void put_unaligned_u64(uint64_t *p_, uint64_t x_)
+{
+    uint8_t *p = (uint8_t *) p_;
+    uint64_t x = ntohll(x_);
+
+    p[0] = x >> 56;
+    p[1] = x >> 48;
+    p[2] = x >> 40;
+    p[3] = x >> 32;
+    p[4] = x >> 24;
+    p[5] = x >> 16;
+    p[6] = x >> 8;
+    p[7] = x;
+}
+#endif
+
+#endif /* unaligned.h */