/* * Copyright (c) 2020 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * * $Id: //eng/uds-releases/jasper/src/uds/bits.h#1 $ */ #ifndef BITS_H #define BITS_H 1 #include "compiler.h" #include "numeric.h" #include "typeDefs.h" /* * These bit stream and bit field utility routines are used for the * non-byte aligned delta indices. * * Bits and bytes are numbered in little endian order. For example: Within * a byte, bit 0 is the least significant bit (0x1), and bit 7 is the most * significant bit (0x80). Within a bit stream, bit 7 is the most * signficant bit of byte 0, and bit 8 is the least significant bit of byte * 1. Within a byte array, a byte's number corresponds to it's index in * the array. * * The implementation assumes that the native machine is little endian, and * that performance is very important. These assumptions match our current * operating environment. */ /** * This is the largest field size supported by getField & setField. Any * field that is larger is not guaranteed to fit in a single, byte aligned * uint32_t. **/ enum { MAX_FIELD_BITS = (sizeof(uint32_t) - 1) * CHAR_BIT + 1 }; /** * This is the number of guard bytes needed at the end of the memory byte * array when using the bit utilities. 3 bytes are needed when getField & * setField access a field, because they will access some "extra" bytes * past the end of the field. And 7 bytes are needed when getBigField & * setBigField access a big field, for the same reason. Note that moveBits * calls getBigField & setBigField. 7 is rewritten to make it clear how it * is derived. **/ enum { POST_FIELD_GUARD_BYTES = sizeof(uint64_t) - 1 }; /** * Get a bit field from a bit stream * * @param memory The base memory byte address * @param offset The bit offset into the memory for the start of the field * @param size The number of bits in the field * * @return the bit field **/ static INLINE unsigned int getField(const byte *memory, uint64_t offset, int size) { const void *addr = memory + offset / CHAR_BIT; return (getUInt32LE(addr) >> (offset % CHAR_BIT)) & ((1 << size) - 1); } /** * Set a bit field in a bit stream * * @param value The value to put into the field * @param memory The base memory byte address * @param offset The bit offset into the memory for the start of the field * @param size The number of bits in the field * * @return the bit field **/ static INLINE void setField(unsigned int value, byte *memory, uint64_t offset, int size) { void *addr = memory + offset / CHAR_BIT; int shift = offset % CHAR_BIT; uint32_t data = getUInt32LE(addr); data &= ~(((1 << size) - 1) << shift); data |= value << shift; storeUInt32LE(addr, data); } /** * Set a bit field in a bit stream to all ones * * @param memory The base memory byte address * @param offset The bit offset into the memory for the start of the field * @param size The number of bits in the field * * @return the bit field **/ static INLINE void setOne(byte *memory, uint64_t offset, int size) { if (size > 0) { byte *addr = memory + offset / CHAR_BIT; int shift = offset % CHAR_BIT; int count = size + shift > CHAR_BIT ? CHAR_BIT - shift : size; *addr++ |= ((1 << count) - 1) << shift; for (size -= count; size > CHAR_BIT; size -= CHAR_BIT) { *addr++ = 0xFF; } if (size) { *addr |= ~(0xFF << size); } } } /** * Set a bit field in a bit stream to all zeros * * @param memory The base memory byte address * @param offset The bit offset into the memory for the start of the field * @param size The number of bits in the field * * @return the bit field **/ static INLINE void setZero(byte *memory, uint64_t offset, int size) { if (size > 0) { byte *addr = memory + offset / CHAR_BIT; int shift = offset % CHAR_BIT; int count = size + shift > CHAR_BIT ? CHAR_BIT - shift : size; *addr++ &= ~(((1 << count) - 1) << shift); for (size -= count; size > CHAR_BIT; size -= CHAR_BIT) { *addr++ = 0; } if (size) { *addr &= 0xFF << size; } } } /** * Get a byte stream from a bit stream, reading a whole number of bytes * from an arbitrary bit boundary. * * @param memory The base memory byte address for the bit stream * @param offset The bit offset of the start of the bit stream * @param destination Where to store the bytes * @param size The number of bytes **/ void getBytes(const byte *memory, uint64_t offset, byte *destination, int size); /** * Store a byte stream into a bit stream, writing a whole number of bytes * to an arbitrary bit boundary. * * @param memory The base memory byte address for the bit stream * @param offset The bit offset of the start of the bit stream * @param source Where to read the bytes * @param size The number of bytes **/ void setBytes(byte *memory, uint64_t offset, const byte *source, int size); /** * Move bits from one field to another. When the fields overlap, behave as * if we first move all the bits from the source to a temporary value, and * then move all the bits from the temporary value to the destination. * * @param sMemory The base source memory byte address * @param source Bit offset into memory for the source start * @param dMemory The base destination memory byte address * @param destination Bit offset into memory for the destination start * @param size The number of bits in the field **/ void moveBits(const byte *sMemory, uint64_t source, byte *dMemory, uint64_t destination, int size); /** * Compare bits from one field to another, testing for sameness * * @param mem1 The base memory byte address (first field) * @param offset1 Bit offset into the memory for the start (first field) * @param mem2 The base memory byte address (second field) * @param offset2 Bit offset into the memory for the start (second field) * @param size The number of bits in the field * * @return true if fields are the same, false if different **/ bool sameBits(const byte *mem1, uint64_t offset1, const byte *mem2, uint64_t offset2, int size) __attribute__((warn_unused_result)); #endif /* BITS_H */