| 91 | */ |
| 92 | template<typename T> |
| 93 | inline typename std::enable_if<std::is_integral<T>::value && |
| 94 | !std::is_signed<T>::value, int>::type |
| 95 | pack(char **buffer, T val) { |
| 96 | // Binary search for the smallest container. It is also worth noting that |
| 97 | // with -O3, the compiler will strip out extraneous if-statements based on T |
| 98 | // For example, if T is uint16_t, it would only leave the t < 1U<<8 check |
| 99 | |
| 100 | //TODO(syang0) Is this too costly vs. a simple for loop? |
| 101 | int numBytes; |
| 102 | if (val < (1UL << 8)) { |
| 103 | numBytes = 1; |
| 104 | } else if (val < (1UL << 16)) { |
| 105 | numBytes = 2; |
| 106 | } else if (val < (1UL << 24)) { |
| 107 | numBytes = 3; |
| 108 | } else if (val < (1UL << 32)) { |
| 109 | numBytes = 4; |
| 110 | } else if (val < (1UL << 40)) { |
| 111 | numBytes = 5; |
| 112 | } else if (val < (1UL << 48)) { |
| 113 | numBytes = 6; |
| 114 | } else if (val < (1UL << 56)) { |
| 115 | numBytes = 7; |
| 116 | } else { |
| 117 | numBytes = 8; |
| 118 | } |
| 119 | |
| 120 | // Although we store the entire value here, we take advantage of the fact |
| 121 | // that x86-64 is little-endian (storing the least significant bits first) |
| 122 | // and lop off the rest by only partially incrementing the buffer pointer |
| 123 | *reinterpret_cast<T*>(*buffer) = val; |
| 124 | *buffer += numBytes; |
| 125 | |
| 126 | return numBytes; |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Below are a series of pack functions that take in a signed integer, |
no outgoing calls