| #ifndef __H_ByteOrder | |
| #define __H_ByteOrder | |
| /* | |
| * Copyright 2008 Google Inc. | |
| * | |
| * 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. | |
| */ | |
| #include "Platform.h" | |
| #include <cstring> | |
| class ByteOrder { | |
| private: | |
| enum FloatByteOrder { | |
| FLOAT_BIG_ENDIAN, | |
| FLOAT_LITTLE_ENDIAN, | |
| // TODO(jat): do we need to consider anything other than straight | |
| // big-endian or little-endian? PDP-11 (irrelevant), ARM (probably | |
| // relevant for mobile devices), MIPS (probably not relevant, but maybe) | |
| // and others have some intermediate endianess. Also, FP-endianess is not | |
| // necessarily the same as that for integers. | |
| }; | |
| #ifdef PLATFORM_FLOAT_ENDIANESS | |
| static const FloatByteOrder floatByteOrder = PLATFORM_FLOAT_ENDIANESS; | |
| #else | |
| FloatByteOrder floatByteOrder; | |
| #endif | |
| typedef union { | |
| float v; | |
| char b[sizeof(float)]; | |
| } FloatUnion; | |
| typedef union { | |
| double v; | |
| char b[sizeof(double)]; | |
| } DoubleUnion; | |
| /** | |
| * Copy src to dest, reversing the order of the bytes. | |
| * Assumes src and dest do not overlap. | |
| */ | |
| void memcpyrev(char* dest, const char* src, size_t n) { | |
| src += n; | |
| while (n-- > 0) { | |
| *dest++ = *--src; | |
| } | |
| } | |
| public: | |
| ByteOrder() { | |
| #ifndef PLATFORM_FLOAT_ENDIANESS | |
| DoubleUnion u; | |
| memset(u.b, 0, sizeof(u.b)); | |
| u.b[0] = (char) 0x80; | |
| u.b[7] = (char) 0x02; | |
| // TODO(jat): add more tests here if we support other endianess | |
| floatByteOrder = u.v > 0 ? FLOAT_LITTLE_ENDIAN : FLOAT_BIG_ENDIAN; | |
| if (Debug::level(Debug::Debugging)) { | |
| std::string str = "Unknown"; | |
| switch (floatByteOrder) { | |
| case FLOAT_LITTLE_ENDIAN: | |
| str = "little-endian"; | |
| break; | |
| case FLOAT_BIG_ENDIAN: | |
| str = "big-endian"; | |
| break; | |
| } | |
| Debug::log(Debug::Debugging) << "Dynamically detected float byte order: " | |
| << str << Debug::flush; | |
| } | |
| #endif | |
| } | |
| void bytesFromDouble(double v, char* bytes) { | |
| DoubleUnion u; | |
| u.v = v; | |
| switch (floatByteOrder) { | |
| case FLOAT_LITTLE_ENDIAN: | |
| memcpyrev(bytes, u.b, sizeof(u.b)); | |
| break; | |
| case FLOAT_BIG_ENDIAN: | |
| memcpy(bytes, u.b, sizeof(u.b)); | |
| break; | |
| } | |
| } | |
| void bytesFromFloat(float v, char* bytes) { | |
| FloatUnion u; | |
| u.v = v; | |
| switch (floatByteOrder) { | |
| case FLOAT_LITTLE_ENDIAN: | |
| memcpyrev(bytes, u.b, sizeof(u.b)); | |
| break; | |
| case FLOAT_BIG_ENDIAN: | |
| memcpy(bytes, u.b, sizeof(u.b)); | |
| break; | |
| } | |
| } | |
| double doubleFromBytes(const char* bytes) { | |
| DoubleUnion u; | |
| switch (floatByteOrder) { | |
| case FLOAT_LITTLE_ENDIAN: | |
| memcpyrev(u.b, bytes, sizeof(u.b)); | |
| break; | |
| case FLOAT_BIG_ENDIAN: | |
| // TODO(jat): find a way to avoid the extra copy while keeping the | |
| // compiler happy. | |
| memcpy(u.b, bytes, sizeof(u.b)); | |
| break; | |
| } | |
| return u.v; | |
| } | |
| float floatFromBytes(const char* bytes) { | |
| FloatUnion u; | |
| switch (floatByteOrder) { | |
| case FLOAT_LITTLE_ENDIAN: | |
| memcpyrev(u.b, bytes, sizeof(u.b)); | |
| break; | |
| case FLOAT_BIG_ENDIAN: | |
| // TODO(jat): find a way to avoid the extra copy while keeping the | |
| // compiler happy. | |
| memcpy(u.b, bytes, sizeof(u.b)); | |
| break; | |
| } | |
| return u.v; | |
| } | |
| }; | |
| #endif |