1 #ifndef SG_UNALIGNED_H 2 #define SG_UNALIGNED_H 3 4 /* 5 * Copyright (c) 2014-2017 Douglas Gilbert. 6 * All rights reserved. 7 * Use of this source code is governed by a BSD-style 8 * license that can be found in the BSD_LICENSE file. 9 */ 10 11 #include <stdint.h> 12 13 #ifdef __cplusplus 14 extern "C" { 15 #endif 16 17 /* Borrowed from the Linux kernel, via mhvtl */ 18 19 /* In the first section below, functions that copy unsigned integers in a 20 * computer's native format, to and from an unaligned big endian sequence of 21 * bytes. Big endian byte format "on the wire" is the default used by SCSI 22 * standards (www.t10.org). Big endian is also the network byte order. */ 23 24 static inline uint16_t __get_unaligned_be16(const uint8_t *p) 25 { 26 return p[0] << 8 | p[1]; 27 } 28 29 static inline uint32_t __get_unaligned_be32(const uint8_t *p) 30 { 31 return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; 32 } 33 34 /* Assume 48 bit value placed in uint64_t */ 35 static inline uint64_t __get_unaligned_be48(const uint8_t *p) 36 { 37 return (uint64_t)__get_unaligned_be16(p) << 32 | 38 __get_unaligned_be32(p + 2); 39 } 40 41 static inline uint64_t __get_unaligned_be64(const uint8_t *p) 42 { 43 return (uint64_t)__get_unaligned_be32(p) << 32 | 44 __get_unaligned_be32(p + 4); 45 } 46 47 static inline void __put_unaligned_be16(uint16_t val, uint8_t *p) 48 { 49 *p++ = val >> 8; 50 *p++ = val; 51 } 52 53 static inline void __put_unaligned_be32(uint32_t val, uint8_t *p) 54 { 55 __put_unaligned_be16(val >> 16, p); 56 __put_unaligned_be16(val, p + 2); 57 } 58 59 /* Assume 48 bit value placed in uint64_t */ 60 static inline void __put_unaligned_be48(uint64_t val, uint8_t *p) 61 { 62 __put_unaligned_be16(val >> 32, p); 63 __put_unaligned_be32(val, p + 2); 64 } 65 66 static inline void __put_unaligned_be64(uint64_t val, uint8_t *p) 67 { 68 __put_unaligned_be32(val >> 32, p); 69 __put_unaligned_be32(val, p + 4); 70 } 71 72 static inline uint16_t sg_get_unaligned_be16(const void *p) 73 { 74 return __get_unaligned_be16((const uint8_t *)p); 75 } 76 77 static inline uint32_t sg_get_unaligned_be24(const void *p) 78 { 79 return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 | 80 ((const uint8_t *)p)[2]; 81 } 82 83 static inline uint32_t sg_get_unaligned_be32(const void *p) 84 { 85 return __get_unaligned_be32((const uint8_t *)p); 86 } 87 88 /* Assume 48 bit value placed in uint64_t */ 89 static inline uint64_t sg_get_unaligned_be48(const void *p) 90 { 91 return __get_unaligned_be48((const uint8_t *)p); 92 } 93 94 static inline uint64_t sg_get_unaligned_be64(const void *p) 95 { 96 return __get_unaligned_be64((const uint8_t *)p); 97 } 98 99 /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than 100 * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is 101 * an 8 byte unsigned integer. */ 102 static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p) 103 { 104 if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) 105 return 0; 106 else { 107 const uint8_t * xp = (const uint8_t *)p; 108 uint64_t res = *xp; 109 110 for (++xp; num_bytes > 1; ++xp, --num_bytes) 111 res = (res << 8) | *xp; 112 return res; 113 } 114 } 115 116 static inline void sg_put_unaligned_be16(uint16_t val, void *p) 117 { 118 __put_unaligned_be16(val, (uint8_t *)p); 119 } 120 121 static inline void sg_put_unaligned_be24(uint32_t val, void *p) 122 { 123 ((uint8_t *)p)[0] = (val >> 16) & 0xff; 124 ((uint8_t *)p)[1] = (val >> 8) & 0xff; 125 ((uint8_t *)p)[2] = val & 0xff; 126 } 127 128 static inline void sg_put_unaligned_be32(uint32_t val, void *p) 129 { 130 __put_unaligned_be32(val, (uint8_t *)p); 131 } 132 133 /* Assume 48 bit value placed in uint64_t */ 134 static inline void sg_put_unaligned_be48(uint64_t val, void *p) 135 { 136 __put_unaligned_be48(val, (uint8_t *)p); 137 } 138 139 static inline void sg_put_unaligned_be64(uint64_t val, void *p) 140 { 141 __put_unaligned_be64(val, (uint8_t *)p); 142 } 143 144 /* Since cdb and parameter blocks are often memset to zero before these 145 * unaligned function partially fill them, then check for a val of zero 146 * and ignore if it is with these variants. */ 147 static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p) 148 { 149 if (val) 150 __put_unaligned_be16(val, (uint8_t *)p); 151 } 152 153 static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p) 154 { 155 if (val) { 156 ((uint8_t *)p)[0] = (val >> 16) & 0xff; 157 ((uint8_t *)p)[1] = (val >> 8) & 0xff; 158 ((uint8_t *)p)[2] = val & 0xff; 159 } 160 } 161 162 static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p) 163 { 164 if (val) 165 __put_unaligned_be32(val, (uint8_t *)p); 166 } 167 168 static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p) 169 { 170 if (val) 171 __put_unaligned_be64(val, (uint8_t *)p); 172 } 173 174 175 /* Below are the little endian equivalents of the big endian functions 176 * above. Little endian is used by ATA, PCI and NVMe. 177 */ 178 179 static inline uint16_t __get_unaligned_le16(const uint8_t *p) 180 { 181 return p[1] << 8 | p[0]; 182 } 183 184 static inline uint32_t __get_unaligned_le32(const uint8_t *p) 185 { 186 return p[3] << 24 | p[2] << 16 | p[1] << 8 | p[0]; 187 } 188 189 static inline uint64_t __get_unaligned_le64(const uint8_t *p) 190 { 191 return (uint64_t)__get_unaligned_le32(p + 4) << 32 | 192 __get_unaligned_le32(p); 193 } 194 195 static inline void __put_unaligned_le16(uint16_t val, uint8_t *p) 196 { 197 *p++ = val; 198 *p++ = val >> 8; 199 } 200 201 static inline void __put_unaligned_le32(uint32_t val, uint8_t *p) 202 { 203 __put_unaligned_le16(val >> 16, p + 2); 204 __put_unaligned_le16(val, p); 205 } 206 207 static inline void __put_unaligned_le64(uint64_t val, uint8_t *p) 208 { 209 __put_unaligned_le32(val >> 32, p + 4); 210 __put_unaligned_le32(val, p); 211 } 212 213 static inline uint16_t sg_get_unaligned_le16(const void *p) 214 { 215 return __get_unaligned_le16((const uint8_t *)p); 216 } 217 218 static inline uint32_t sg_get_unaligned_le24(const void *p) 219 { 220 return (uint32_t)__get_unaligned_le16((const uint8_t *)p) | 221 ((const uint8_t *)p)[2] << 16; 222 } 223 224 static inline uint32_t sg_get_unaligned_le32(const void *p) 225 { 226 return __get_unaligned_le32((const uint8_t *)p); 227 } 228 229 /* Assume 48 bit value placed in uint64_t */ 230 static inline uint64_t sg_get_unaligned_le48(const void *p) 231 { 232 return (uint64_t)__get_unaligned_le16((const uint8_t *)p + 4) << 32 | 233 __get_unaligned_le32((const uint8_t *)p); 234 } 235 236 static inline uint64_t sg_get_unaligned_le64(const void *p) 237 { 238 return __get_unaligned_le64((const uint8_t *)p); 239 } 240 241 /* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than 242 * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is 243 * an 8 byte unsigned integer. */ 244 static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p) 245 { 246 if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t))) 247 return 0; 248 else { 249 const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1); 250 uint64_t res = *xp; 251 252 for (--xp; num_bytes > 1; --xp, --num_bytes) 253 res = (res << 8) | *xp; 254 return res; 255 } 256 } 257 258 static inline void sg_put_unaligned_le16(uint16_t val, void *p) 259 { 260 __put_unaligned_le16(val, (uint8_t *)p); 261 } 262 263 static inline void sg_put_unaligned_le24(uint32_t val, void *p) 264 { 265 ((uint8_t *)p)[2] = (val >> 16) & 0xff; 266 ((uint8_t *)p)[1] = (val >> 8) & 0xff; 267 ((uint8_t *)p)[0] = val & 0xff; 268 } 269 270 static inline void sg_put_unaligned_le32(uint32_t val, void *p) 271 { 272 __put_unaligned_le32(val, (uint8_t *)p); 273 } 274 275 /* Assume 48 bit value placed in uint64_t */ 276 static inline void sg_put_unaligned_le48(uint64_t val, void *p) 277 { 278 ((uint8_t *)p)[5] = (val >> 40) & 0xff; 279 ((uint8_t *)p)[4] = (val >> 32) & 0xff; 280 ((uint8_t *)p)[3] = (val >> 24) & 0xff; 281 ((uint8_t *)p)[2] = (val >> 16) & 0xff; 282 ((uint8_t *)p)[1] = (val >> 8) & 0xff; 283 ((uint8_t *)p)[0] = val & 0xff; 284 } 285 286 static inline void sg_put_unaligned_le64(uint64_t val, void *p) 287 { 288 __put_unaligned_le64(val, (uint8_t *)p); 289 } 290 291 /* Since cdb and parameter blocks are often memset to zero before these 292 * unaligned function partially fill them, then check for a val of zero 293 * and ignore if it is with these variants. */ 294 static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p) 295 { 296 if (val) 297 __put_unaligned_le16(val, (uint8_t *)p); 298 } 299 300 static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p) 301 { 302 if (val) { 303 ((uint8_t *)p)[2] = (val >> 16) & 0xff; 304 ((uint8_t *)p)[1] = (val >> 8) & 0xff; 305 ((uint8_t *)p)[0] = val & 0xff; 306 } 307 } 308 309 static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p) 310 { 311 if (val) 312 __put_unaligned_le32(val, (uint8_t *)p); 313 } 314 315 static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p) 316 { 317 if (val) 318 __put_unaligned_le64(val, (uint8_t *)p); 319 } 320 321 #ifdef __cplusplus 322 } 323 #endif 324 325 #endif /* SG_UNALIGNED_H */ 326