src/kudu/gutil/endian.h (211 lines of code) (raw):

// Copyright 2005 Google Inc. // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you 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. // // --- // // // Utility functions that depend on bytesex. We define htonll and ntohll, // as well as "Google" versions of all the standards: ghtonl, ghtons, and // so on. These functions do exactly the same as their standard variants, // but don't require including the dangerous netinet/in.h. // // Buffer routines will copy to and from buffers without causing // a bus error when the architecture requires differnt byte alignments #ifndef UTIL_ENDIAN_ENDIAN_H_ #define UTIL_ENDIAN_ENDIAN_H_ #include <assert.h> #include "kudu/gutil/int128.h" #include "kudu/gutil/integral_types.h" #include "kudu/gutil/port.h" inline uint64 gbswap_64(uint64 host_int) { #if defined(__GNUC__) && defined(__x86_64__) && !defined(__APPLE__) // Adapted from /usr/include/byteswap.h. Not available on Mac. if (__builtin_constant_p(host_int)) { return __bswap_constant_64(host_int); } else { uint64 result; __asm__("bswap %0" : "=r" (result) : "0" (host_int)); return result; } #elif defined(bswap_64) return bswap_64(host_int); #else return static_cast<uint64>(bswap_32(static_cast<uint32>(host_int >> 32))) | (static_cast<uint64>(bswap_32(static_cast<uint32>(host_int))) << 32); #endif // bswap_64 } inline unsigned __int128 gbswap_128(unsigned __int128 host_int) { return static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int >> 64))) | (static_cast<unsigned __int128>(bswap_64(static_cast<uint64>(host_int))) << 64); } #ifdef IS_LITTLE_ENDIAN // Definitions for ntohl etc. that don't require us to include // netinet/in.h. We wrap bswap_32 and bswap_16 in functions rather // than just #defining them because in debug mode, gcc doesn't // correctly handle the (rather involved) definitions of bswap_32. // gcc guarantees that inline functions are as fast as macros, so // this isn't a performance hit. inline uint16 ghtons(uint16 x) { return bswap_16(x); } inline uint32 ghtonl(uint32 x) { return bswap_32(x); } inline uint64 ghtonll(uint64 x) { return gbswap_64(x); } #elif defined IS_BIG_ENDIAN // These definitions are simpler on big-endian machines // These are functions instead of macros to avoid self-assignment warnings // on calls such as "i = ghtnol(i);". This also provides type checking. inline uint16 ghtons(uint16 x) { return x; } inline uint32 ghtonl(uint32 x) { return x; } inline uint64 ghtonll(uint64 x) { return x; } #else #error "Unsupported bytesex: Either IS_BIG_ENDIAN or IS_LITTLE_ENDIAN must be defined" // NOLINT #endif // bytesex // ntoh* and hton* are the same thing for any size and bytesex, // since the function is an involution, i.e., its own inverse. #define gntohl(x) ghtonl(x) #define gntohs(x) ghtons(x) #define gntohll(x) ghtonll(x) #if !defined(__APPLE__) // This one is safe to take as it's an extension #define htonll(x) ghtonll(x) #define ntohll(x) htonll(x) #endif // Utilities to convert numbers between the current hosts's native byte // order and little-endian byte order // // Load/Store methods are alignment safe class LittleEndian { public: // Conversion functions. #ifdef IS_LITTLE_ENDIAN static uint16 FromHost16(uint16 x) { return x; } static uint16 ToHost16(uint16 x) { return x; } static uint32 FromHost32(uint32 x) { return x; } static uint32 ToHost32(uint32 x) { return x; } static uint64 FromHost64(uint64 x) { return x; } static uint64 ToHost64(uint64 x) { return x; } static unsigned __int128 FromHost128(unsigned __int128 x) { return x; } static unsigned __int128 ToHost128(unsigned __int128 x) { return x; } static bool IsLittleEndian() { return true; } #elif defined IS_BIG_ENDIAN static uint16 FromHost16(uint16 x) { return bswap_16(x); } static uint16 ToHost16(uint16 x) { return bswap_16(x); } static uint32 FromHost32(uint32 x) { return bswap_32(x); } static uint32 ToHost32(uint32 x) { return bswap_32(x); } static uint64 FromHost64(uint64 x) { return gbswap_64(x); } static uint64 ToHost64(uint64 x) { return gbswap_64(x); } static bool IsLittleEndian() { return false; } #endif /* ENDIAN */ // Functions to do unaligned loads and stores in little-endian order. static uint16 Load16(const void *p) { return ToHost16(UNALIGNED_LOAD16(p)); } static void Store16(void *p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); } static uint32 Load32(const void *p) { return ToHost32(UNALIGNED_LOAD32(p)); } static void Store32(void *p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); } static uint64 Load64(const void *p) { return ToHost64(UNALIGNED_LOAD64(p)); } // Build a uint64 from 1-8 bytes. // 8 * len least significant bits are loaded from the memory with // LittleEndian order. The 64 - 8 * len most significant bits are // set all to 0. // In latex-friendly words, this function returns: // $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned. // // This function is equivalent with: // uint64 val = 0; // memcpy(&val, p, len); // return ToHost64(val); // TODO(user): write a small benchmark and benchmark the speed // of a memcpy based approach. // // For speed reasons this function does not work for len == 0. // The caller needs to guarantee that 1 <= len <= 8. static uint64 Load64VariableLength(const void * const p, int len) { assert(len >= 1 && len <= 8); const char * const buf = static_cast<const char * const>(p); uint64 val = 0; --len; do { val = (val << 8) | buf[len]; // (--len >= 0) is about 10 % faster than (len--) in some benchmarks. } while (--len >= 0); // No ToHost64(...) needed. The bytes are accessed in little-endian manner // on every architecture. return val; } static void Store64(void *p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); } static uint128 Load128(const void *p) { return uint128( ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64 *>(p) + 1)), ToHost64(UNALIGNED_LOAD64(p))); } static void Store128(void *p, const uint128 v) { UNALIGNED_STORE64(p, FromHost64(Uint128Low64(v))); UNALIGNED_STORE64(reinterpret_cast<uint64 *>(p) + 1, FromHost64(Uint128High64(v))); } // Build a uint128 from 1-16 bytes. // 8 * len least significant bits are loaded from the memory with // LittleEndian order. The 128 - 8 * len most significant bits are // set all to 0. static uint128 Load128VariableLength(const void *p, int len) { if (len <= 8) { return uint128(Load64VariableLength(p, len)); } else { return uint128( Load64VariableLength(static_cast<const char *>(p) + 8, len - 8), Load64(p)); } } // Load & Store in machine's word size. static uword_t LoadUnsignedWord(const void *p) { if (sizeof(uword_t) == 8) return Load64(p); else return Load32(p); } static void StoreUnsignedWord(void *p, uword_t v) { if (sizeof(v) == 8) Store64(p, v); else Store32(p, v); } }; // Utilities to convert numbers between the current hosts's native byte // order and big-endian byte order (same as network byte order) // // Load/Store methods are alignment safe class BigEndian { public: #ifdef IS_LITTLE_ENDIAN static uint16 FromHost16(uint16 x) { return bswap_16(x); } static uint16 ToHost16(uint16 x) { return bswap_16(x); } static uint32 FromHost32(uint32 x) { return bswap_32(x); } static uint32 ToHost32(uint32 x) { return bswap_32(x); } static uint64 FromHost64(uint64 x) { return gbswap_64(x); } static uint64 ToHost64(uint64 x) { return gbswap_64(x); } static unsigned __int128 FromHost128(unsigned __int128 x) { return gbswap_128(x); } static unsigned __int128 ToHost128(unsigned __int128 x) { return gbswap_128(x); } static bool IsLittleEndian() { return true; } #elif defined IS_BIG_ENDIAN static uint16 FromHost16(uint16 x) { return x; } static uint16 ToHost16(uint16 x) { return x; } static uint32 FromHost32(uint32 x) { return x; } static uint32 ToHost32(uint32 x) { return x; } static uint64 FromHost64(uint64 x) { return x; } static uint64 ToHost64(uint64 x) { return x; } static uint128 FromHost128(uint128 x) { return x; } static uint128 ToHost128(uint128 x) { return x; } static bool IsLittleEndian() { return false; } #endif /* ENDIAN */ // Functions to do unaligned loads and stores in little-endian order. static uint16 Load16(const void *p) { return ToHost16(UNALIGNED_LOAD16(p)); } static void Store16(void *p, uint16 v) { UNALIGNED_STORE16(p, FromHost16(v)); } static uint32 Load32(const void *p) { return ToHost32(UNALIGNED_LOAD32(p)); } static void Store32(void *p, uint32 v) { UNALIGNED_STORE32(p, FromHost32(v)); } static uint64 Load64(const void *p) { return ToHost64(UNALIGNED_LOAD64(p)); } // Build a uint64 from 1-8 bytes. // 8 * len least significant bits are loaded from the memory with // BigEndian order. The 64 - 8 * len most significant bits are // set all to 0. // In latex-friendly words, this function returns: // $\sum_{i=0}^{len-1} p[i] 256^{i}$, where p[i] is unsigned. // // This function is equivalent with: // uint64 val = 0; // memcpy(&val, p, len); // return ToHost64(val); // TODO(user): write a small benchmark and benchmark the speed // of a memcpy based approach. // // For speed reasons this function does not work for len == 0. // The caller needs to guarantee that 1 <= len <= 8. static uint64 Load64VariableLength(const void * const p, int len) { assert(len >= 1 && len <= 8); uint64 val = Load64(p); uint64 mask = 0; --len; do { mask = (mask << 8) | 0xff; // (--len >= 0) is about 10 % faster than (len--) in some benchmarks. } while (--len >= 0); return val & mask; } static void Store64(void *p, uint64 v) { UNALIGNED_STORE64(p, FromHost64(v)); } static uint128 Load128(const void *p) { return uint128( ToHost64(UNALIGNED_LOAD64(p)), ToHost64(UNALIGNED_LOAD64(reinterpret_cast<const uint64 *>(p) + 1))); } static void Store128(void *p, const uint128 v) { UNALIGNED_STORE64(p, FromHost64(Uint128High64(v))); UNALIGNED_STORE64(reinterpret_cast<uint64 *>(p) + 1, FromHost64(Uint128Low64(v))); } // Build a uint128 from 1-16 bytes. // 8 * len least significant bits are loaded from the memory with // BigEndian order. The 128 - 8 * len most significant bits are // set all to 0. static uint128 Load128VariableLength(const void *p, int len) { if (len <= 8) { return uint128(Load64VariableLength(static_cast<const char *>(p)+8, len)); } else { return uint128( Load64VariableLength(p, len-8), Load64(static_cast<const char *>(p)+8)); } } // Load & Store in machine's word size. static uword_t LoadUnsignedWord(const void *p) { if (sizeof(uword_t) == 8) return Load64(p); else return Load32(p); } static void StoreUnsignedWord(void *p, uword_t v) { if (sizeof(uword_t) == 8) Store64(p, v); else Store32(p, v); } }; // BigEndian // Network byte order is big-endian typedef BigEndian NetworkByteOrder; #endif // UTIL_ENDIAN_ENDIAN_H_