mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-11-02 12:53:15 +00:00
673 lines
20 KiB
C++
673 lines
20 KiB
C++
/*
|
|
* Copyright 2011, 2014 Range Networks, Inc.
|
|
*
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#define LOG_GROUP LogGroup::GPRS // Can set Log.Level.GPRS for debugging
|
|
|
|
#include "ByteVector.h"
|
|
|
|
// Set the char[2] array at ip to a 16-bit int value, swizzling bytes as needed for network order.
|
|
void sethtons(ByteType *cp,unsigned value)
|
|
{
|
|
uint16_t tmp = htons(value);
|
|
ByteType *tp = (ByteType*)&tmp;
|
|
cp[0]=tp[0]; cp[1]=tp[1]; // Overkill but safe.
|
|
}
|
|
|
|
// Set the char[4] array at ip to a 32-bit int value, swizzling bytes as needed for network order.
|
|
void sethtonl(ByteType *cp,unsigned value)
|
|
{
|
|
uint32_t tmp = htonl(value);
|
|
ByteType *tp = (ByteType*)&tmp;
|
|
cp[0]=tp[0]; cp[1]=tp[1]; cp[2]=tp[2]; cp[3]=tp[3];
|
|
}
|
|
|
|
uint16_t getntohs(ByteType *cp)
|
|
{
|
|
uint16_t tmp;
|
|
ByteType *tp = (ByteType*)&tmp;
|
|
tp[0]=cp[0]; tp[1]=cp[1];
|
|
return ntohs(tmp);
|
|
}
|
|
|
|
uint32_t getntohl(ByteType *cp)
|
|
{
|
|
uint32_t tmp;
|
|
ByteType *tp = (ByteType*)&tmp;
|
|
tp[0]=cp[0]; tp[1]=cp[1]; tp[2]=cp[2]; tp[3]=cp[3];
|
|
return ntohl(tmp);
|
|
}
|
|
|
|
void ByteVector::clear()
|
|
{
|
|
if (mData) {
|
|
#if BYTEVECTOR_REFCNT
|
|
if (decRefCnt() <= 0) { delete[] mData; RN_MEMCHKDEL(ByteVectorData) }
|
|
#else
|
|
delete[] mData;
|
|
#endif
|
|
}
|
|
mSizeBits = 0;
|
|
mData = NULL;
|
|
}
|
|
|
|
void ByteVector::init(size_t size)
|
|
{
|
|
//mBitInd = 0;
|
|
if (size == 0) {
|
|
mData = mStart = 0;
|
|
} else {
|
|
#if BYTEVECTOR_REFCNT
|
|
RN_MEMCHKNEW(ByteVectorData)
|
|
mData = new ByteType[size + mDataOffset];
|
|
setRefCnt(1);
|
|
mStart = mData + mDataOffset;
|
|
#else
|
|
mData = new ByteType[size];
|
|
mStart = mData;
|
|
#endif
|
|
}
|
|
mAllocEnd = mStart + size;
|
|
mSizeBits = size*8;
|
|
}
|
|
|
|
// Make a full memory copy of other.
|
|
// We clone only the filled in area, not the unused allocated area.
|
|
void ByteVector::clone(const ByteVector &other)
|
|
{
|
|
clear();
|
|
init(other.size());
|
|
memcpy(mStart,other.mStart,other.size());
|
|
}
|
|
|
|
// Make this a copy of other.
|
|
// It it owns memory, share it using refcnts.
|
|
// Formerly: moved ownership of allocated data to ourself.
|
|
void ByteVector::dup(const ByteVector &other)
|
|
{
|
|
clear();
|
|
mData=other.mData;
|
|
mStart=other.mStart;
|
|
mSizeBits=other.mSizeBits;
|
|
mAllocEnd = other.mAllocEnd;
|
|
//mBitInd = other.mBitInd;
|
|
#if BYTEVECTOR_REFCNT
|
|
if (mData) incRefCnt();
|
|
#else
|
|
other.mData=NULL;
|
|
#endif
|
|
}
|
|
|
|
// Return a segment of a ByteVector that shares the same memory as the original.
|
|
ByteVector ByteVector::segment(size_t start, size_t span) const
|
|
{
|
|
#if NEW_SEGMENT_SEMANTICS
|
|
BVASSERT(start+span <= size());
|
|
ByteVector result(*this);
|
|
result.mStart = mStart + start;
|
|
result.mSizeBits = span*8;
|
|
//result.mEnd = result.mStart + span;
|
|
//BVASSERT(result.mEnd<=mEnd);
|
|
return result;
|
|
#else
|
|
ByteType* wStart = mStart + start;
|
|
ByteType* wEnd = wStart + span;
|
|
BVASSERT(wEnd<=mEnd);
|
|
return ByteVector(wStart,wEnd);
|
|
#endif
|
|
}
|
|
|
|
// This returns a segment that does not share ownership of the original memory,
|
|
// so when the original is deleted, this is destroyed also, and without warning.
|
|
// Very easy to insert bugs in your code, which is why it is called segmentTemp to indicate
|
|
// that it is a ByteVector for temporary use only.
|
|
const ByteVectorTemp ByteVector::segmentTemp(size_t start, size_t span) const
|
|
{
|
|
BVASSERT(start+span <= size());
|
|
ByteType* wStart = mStart + start;
|
|
ByteType* wEnd = wStart + span;
|
|
//BVASSERT(wEnd<=mEnd);
|
|
return ByteVectorTemp(wStart,wEnd);
|
|
}
|
|
|
|
// Copy other to this starting at start.
|
|
// The 'this' ByteVector must be allocated large enough to hold other.
|
|
// Unlike Vector, the size() is increased to make it fit, up to the allocated size.
|
|
void ByteVector::setSegment(size_t start, ByteVector&other)
|
|
{
|
|
BVASSERT(start <= size()); // If start == size(), nothing is copied.
|
|
BVASSERT(bitind() == 0); // This function only allowed on byte-aligned data.
|
|
ByteType* base = mStart + start;
|
|
int othersize = other.size();
|
|
BVASSERT(mAllocEnd - base >= othersize);
|
|
memcpy(base,other.mStart,othersize);
|
|
//if (mEnd - base < othersize) { mEnd = base + othersize; } // Grow size() if necessary.
|
|
if (mSizeBits/8 < start+othersize) { mSizeBits = (start+othersize)*8; }
|
|
}
|
|
|
|
// Copy part of this ByteVector to a segment of another.
|
|
// The specified span must not exceed our size, and it must fit in the target ByteVector.
|
|
// Unlike Vector, the size() of other is increased to make it fit, up to the allocated size.
|
|
void ByteVector::copyToSegment(ByteVector& other, size_t start, size_t span) const
|
|
{
|
|
ByteType* base = other.mStart + start;
|
|
BVASSERT(start <= other.size()); // If start == size(), nothing is copied.
|
|
BVASSERT(base+span<=other.mAllocEnd);
|
|
//BVASSERT(mStart+span<=mEnd);
|
|
//BVASSERT(base+span<=other.mAllocEnd);
|
|
memcpy(base,mStart,span);
|
|
//if (base+span > other.mEnd) { other.mEnd = base+span; } // Increase other.size() if necessary.
|
|
if (other.size() < start+span) { other.mSizeBits = (start+span)*8; }
|
|
}
|
|
|
|
/** Copy all of this Vector to a segment of another Vector. */
|
|
void ByteVector::copyToSegment(ByteVector& other, size_t start /*=0*/) const
|
|
{
|
|
copyToSegment(other,start,size());
|
|
}
|
|
|
|
|
|
void ByteVector::append(const ByteType *bytes, unsigned len)
|
|
{
|
|
memcpy(&mStart[grow(len)],bytes,len);
|
|
}
|
|
|
|
// Does change size().
|
|
void ByteVector::appendFill(ByteType byte, size_t span)
|
|
{
|
|
memset(&mStart[grow(span)],byte,span);
|
|
}
|
|
|
|
void ByteVector::append(const ByteVector&other)
|
|
{
|
|
append(other.mStart,other.size());
|
|
//BVASSERT(othersize <= mAllocEnd - mEnd);
|
|
//memcpy(mEnd,other.mStart,othersize);
|
|
//mEnd += othersize;
|
|
}
|
|
|
|
// append a BitVector to this, converting the BitVector back to bytes.
|
|
void ByteVector::append(const BitVector&other)
|
|
{
|
|
int othersizebits = other.size();
|
|
int bitindex = bitind();
|
|
if (bitindex) {
|
|
// Heck with it. Optimize this if you want to use it.
|
|
int iself = growBits(othersizebits); // index into this.
|
|
int iother = 0; // index into other
|
|
// First partial byte
|
|
int rem = 8-bitindex;
|
|
if (rem > othersizebits) rem = othersizebits;
|
|
setField(iself,other.peekField(iother,rem),rem);
|
|
iself += rem; iother += rem;
|
|
// Copy whole bytes.
|
|
for (; othersizebits-iother>=8; iother+=8, iself+=8) {
|
|
setByte(iself/8,other.peekField(iother,8));
|
|
}
|
|
// Final partial byte.
|
|
rem = othersizebits-iother;
|
|
if (rem) {
|
|
setField(iself,other.peekField(iother,rem),rem);
|
|
}
|
|
return;
|
|
} else {
|
|
other.pack(&mStart[growBits(othersizebits)/8]);
|
|
}
|
|
//BVASSERT(othersize <= mAllocEnd - mEnd);
|
|
//other.pack(mEnd);
|
|
//mEnd += othersize;
|
|
}
|
|
|
|
// Length Indicator: GSM08.16 sec 10.1.2
|
|
// The length indicator may be 1 or 2 bytes, depending on bit 8,
|
|
// which is 0 to indicate a 15 bit length, or 1 to indicate a 7 bit length.
|
|
unsigned ByteVector::readLI(size_t &wp)
|
|
{
|
|
unsigned byte1 = getByte(wp++);
|
|
if (byte1 & 0x80) { return byte1 & 0x7f; }
|
|
return (byte1 * 256) + getByte(wp++);
|
|
}
|
|
|
|
// This is a two byte length indicator as per GSM 08.16 10.1.2
|
|
void ByteVector::appendLI(unsigned len)
|
|
{
|
|
if (len < 255) {
|
|
appendByte(len | 0x80);
|
|
} else {
|
|
BVASSERT(len <= 32767);
|
|
appendByte(0x7f&(len>>8));
|
|
appendByte(0xff&(len>>8));
|
|
}
|
|
}
|
|
|
|
// The inverse of trimRight. Like an append but the new area is uninitialized.
|
|
void ByteVector::growRight(unsigned amt)
|
|
{
|
|
BVASSERT(!bitind());
|
|
BVASSERT(amt <= size());
|
|
mSizeBits += 8*amt;
|
|
}
|
|
|
|
// The inverse of trimLeft
|
|
ByteType* ByteVector::growLeft(unsigned amt)
|
|
{
|
|
ByteType *newstart = mStart - amt;
|
|
BVASSERT(newstart >= mData + mDataOffset);
|
|
mSizeBits += 8*amt;
|
|
return mStart = newstart;
|
|
}
|
|
|
|
void ByteVector::trimLeft(unsigned amt)
|
|
{
|
|
BVASSERT(amt <= size());
|
|
mStart += amt;
|
|
mSizeBits -= 8*amt;
|
|
}
|
|
|
|
void ByteVector::trimRight(unsigned amt)
|
|
{
|
|
BVASSERT(!bitind());
|
|
BVASSERT(amt <= size());
|
|
//mEnd -= amt;
|
|
mSizeBits -= 8*amt;
|
|
}
|
|
|
|
// For appending.
|
|
// Grow the vector by the specified amount of bytes and return the index of that location.
|
|
unsigned ByteVector::grow(unsigned amt)
|
|
{
|
|
unsigned writeIndex = sizeBytes(); // relative to mStart.
|
|
BVASSERT(bitind() == 0); // If it is not byte-aligned, cant use these functions; use setField instead.
|
|
setSizeBits(mSizeBits + 8*amt);
|
|
//BVASSERT(amt < sizeRemaining());
|
|
//mSizeBits += 8*amt;
|
|
//unsigned writeIndex = mEnd - mStart;
|
|
//mEnd += amt;
|
|
//BVASSERT(mEnd <= mAllocEnd);
|
|
return writeIndex;
|
|
}
|
|
|
|
// For appending.
|
|
// Grow the vector by amt in bits; return the old size in bits.
|
|
unsigned ByteVector::growBits(unsigned amt)
|
|
{
|
|
int oldsizebits = sizeBits();
|
|
setSizeBits(oldsizebits + amt);
|
|
return oldsizebits;
|
|
}
|
|
|
|
|
|
// GSM04.60 10.0b.3.1: Note that fields in RLC blocks use network order,
|
|
// meaning most significant byte first (cause they started on Sun workstations.)
|
|
// It is faster to use htons, etc, than unpacking these ourselves.
|
|
void ByteVector::setUInt16(size_t writeIndex,unsigned value) { // 2 byte value
|
|
BVASSERT(writeIndex <= size() - 2);
|
|
sethtons(&mStart[writeIndex],value);
|
|
}
|
|
|
|
void ByteVector::setUInt32(size_t writeIndex, unsigned value) { // 4 byte value
|
|
BVASSERT(writeIndex <= size() - 4);
|
|
sethtonl(&mStart[writeIndex],value);
|
|
}
|
|
|
|
// Does not change size().
|
|
void ByteVector::fill(ByteType byte, size_t start, size_t span) {
|
|
ByteType *dp=mStart+start;
|
|
ByteType *end=dp+span;
|
|
BVASSERT(end<=mAllocEnd);
|
|
while (dp<end) { *dp++ = byte; }
|
|
}
|
|
|
|
unsigned ByteVector::getUInt16(size_t readIndex) const { // 2 byte value
|
|
BVASSERT(readIndex <= size() - 2);
|
|
uint16_t tmp;
|
|
ByteType *tp = (ByteType*)&tmp;
|
|
ByteType *cp = &mStart[readIndex];
|
|
tp[0]=cp[0]; tp[1]=cp[1];
|
|
return ntohs(tmp);
|
|
}
|
|
unsigned ByteVector::readUInt16(size_t &rp) {
|
|
unsigned result = getUInt16(rp);
|
|
rp+=2;
|
|
return result;
|
|
}
|
|
|
|
unsigned ByteVector::getUInt32(size_t readIndex) const { // 4 byte value
|
|
BVASSERT(readIndex <= size() - 4);
|
|
uint32_t tmp;
|
|
ByteType *tp = (unsigned char*)&tmp;
|
|
ByteType *cp = &mStart[readIndex];
|
|
tp[0]=cp[0]; tp[1]=cp[1]; tp[2]=cp[2]; tp[3]=cp[3];
|
|
return ntohl(tmp);
|
|
}
|
|
unsigned ByteVector::readUInt32(size_t &rp) {
|
|
unsigned result = getUInt32(rp);
|
|
rp+=4;
|
|
return result;
|
|
}
|
|
|
|
|
|
// This function returns just the payload part as a hex string.
|
|
// Also works if the ByteVector is encoded as BCD.
|
|
// Probably not efficient, but we are using C++.
|
|
std::string ByteVector::hexstr() const
|
|
{
|
|
int b = 0, numBits = (int) sizeBits(); // Must be int, not unsigned
|
|
std::string ss;
|
|
while (numBits > 0) {
|
|
char ch = getNibble(b,1);
|
|
ss.push_back(ch + (ch > 9 ? ('A'-10) : '0'));
|
|
numBits -= 4;
|
|
if (numBits >= 4) {
|
|
ch = getNibble(b,0);
|
|
ss.push_back(ch + (ch > 9 ? ('A'-10) : '0'));
|
|
}
|
|
b++;
|
|
numBits -= 4;
|
|
}
|
|
return ss;
|
|
}
|
|
|
|
// This function returns "ByteVector(size=... data:...)"
|
|
std::string ByteVector::str() const
|
|
{
|
|
std::ostringstream ss;
|
|
ss << *this;
|
|
return ss.str();
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream&os, const ByteVector&vec)
|
|
{
|
|
int i, size=vec.size(); char buf[10];
|
|
os <<"ByteVector(size=" <<size <<" data:";
|
|
for (i=0; i < size; i++) {
|
|
sprintf(buf," %02x",vec.getByte(i));
|
|
os << buf;
|
|
}
|
|
os <<")";
|
|
return os;
|
|
}
|
|
|
|
static ByteType bitMasks[8] = { 0x80, 0x40, 0x20, 0x10, 8, 4, 2, 1 };
|
|
|
|
// Get a bit from the specified byte, numbered like this:
|
|
// bits: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
bool ByteVector::getBit2(size_t byteIndex, unsigned bitIndex) const
|
|
{
|
|
BVASSERT(bitIndex >= 0 && bitIndex <= 7);
|
|
//return !!(getByte(byteIndex) & (1 << (7-bitIndex)));
|
|
return !!(getByte(byteIndex) & bitMasks[bitIndex]);
|
|
}
|
|
|
|
// Get a bit from the specified byte, numbered like this:
|
|
// bits: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
void ByteVector::setBit2(size_t byteIndex, unsigned bitIndex, unsigned val)
|
|
{
|
|
BVASSERT(bitIndex >= 0 && bitIndex <= 7);
|
|
BVASSERT(byteIndex < size());
|
|
ByteType mask = bitMasks[bitIndex];
|
|
mStart[byteIndex] = val ? (mStart[byteIndex] | mask) : (mStart[byteIndex] & ~mask);
|
|
}
|
|
|
|
#ifndef MIN
|
|
#define MIN(a,b) ((a)<(b)?(a):(b))
|
|
#endif
|
|
|
|
// Write a bit field starting at specified byte and bit, each numbered from 0
|
|
void ByteVector::setField2(size_t byteIndex, size_t bitIndex, uint64_t value,unsigned lengthBits)
|
|
{
|
|
BVASSERT(bitIndex >= 0 && bitIndex <= 7);
|
|
// Example: bitIndex = 2, length = 2;
|
|
// 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
// 0 0 X X 0 0 0 0
|
|
// endpos = 4; nbytes = 0; lastbit = 4; nbits = 2; mask = 3; shift = 4;
|
|
|
|
unsigned endpos = bitIndex + lengthBits; // 1 past the 0-based index of the last bit.
|
|
unsigned nbytes = (endpos-1) / 8; // number of bytes that will be modified, minus 1.
|
|
ByteType *dp = mStart + byteIndex + nbytes;
|
|
|
|
unsigned lastbit = endpos % 8; // index of first bit not to be replaced, or 0.
|
|
// Number of bits to modify in the current byte, starting at the last byte.
|
|
unsigned nbits = lastbit ? MIN(lengthBits,lastbit) : MIN(lengthBits,8);
|
|
|
|
for (int len = lengthBits; len > 0; dp--) {
|
|
// Mask of number of bits to be modified in this byte, starting from LSB.
|
|
unsigned mask = (1 << nbits) - 1;
|
|
ByteType val = value & mask;
|
|
value >>= nbits;
|
|
if (lastbit) {
|
|
// Shift val and mask so they are aligned with the bits to modify in the last byte,
|
|
// noting that we modify the last byte first, since we work backwards.
|
|
int shift = 8 - lastbit;
|
|
mask <<= shift;
|
|
val <<= shift;
|
|
}
|
|
*dp = (*dp & ~mask) | (val & mask);
|
|
len -= nbits;
|
|
nbits = MIN(len,8);
|
|
|
|
lastbit = 0;
|
|
}
|
|
}
|
|
|
|
void ByteVector::appendField(uint64_t value,unsigned lengthBits)
|
|
{
|
|
setField(growBits(lengthBits),value,lengthBits);
|
|
/*** old
|
|
int endpos = mBitInd + lengthBits; // 1 past the 0-based index of the last bit.
|
|
int nbytes = (endpos-1) / 8; // number of new bytes needed.
|
|
if (mBitInd == 0) nbytes++; // if at 0, the next byte has not been alloced yet.
|
|
setField2(grow(nbytes),mBitInd,value,lengthBits);
|
|
mBitInd = endpos % 8;
|
|
***/
|
|
}
|
|
|
|
// Read a bit field starting at specified byte and bit, each numbered from 0.
|
|
// Bit numbering is from high to low, like this:
|
|
// getField bitIndex: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
|
// Note that this is inverted with respect to the numbering scheme used
|
|
// in many GSM specs, which looks like this:
|
|
// GSM specs: 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1
|
|
// Note that some GSM specs use low-to-high and some use high-to-low numbering.
|
|
// Generally, where the BitVector class is used they use low-to-high numbering,
|
|
// which is rectified in the FEC classes by the byteswapping the BitVector before being used.
|
|
uint64_t ByteVector::getField2(size_t byteIndex, size_t bitIndex, unsigned lengthBits) const
|
|
{
|
|
ByteType *dp = mStart + byteIndex;
|
|
int len = (int) lengthBits;
|
|
BVASSERT(bitIndex >= 0 && bitIndex <= 7);
|
|
// Get first byte:
|
|
// This was for bitIndex running from low bit to high bit:
|
|
// int nbits = bitIndex+1; // Number of bits saved from byte.
|
|
// This is for bitIndex running from 0=>high bit to 7=>low bit:
|
|
int nbits = 8-bitIndex; // Number of bits saved from byte, ignoring len restriction.
|
|
// Example: bitIndex=3 => 0 | 0 | 0 | X | X | X | X | X => AND with 0x1f
|
|
|
|
uint64_t accum = *dp++ & (0x0ff >> (8-nbits)); // Preserve right-most bits.
|
|
if (len < nbits) { accum >>= (nbits - len); return accum; }
|
|
len -= nbits;
|
|
|
|
// Get the full bytes:
|
|
for (; len >= 8; len -= 8) { accum = (accum << 8) | *dp++; }
|
|
|
|
// Append high bits of last byte:
|
|
if (len>0) { accum = (accum << len) | (*dp >> (8-len)); }
|
|
return accum;
|
|
}
|
|
|
|
// This is static - there is no 'this' argument.
|
|
int ByteVector::compare(const ByteVector &bv1, const ByteVector &bv2)
|
|
{
|
|
unsigned bv1size = bv1.sizeBits(), bv2size = bv2.sizeBits();
|
|
unsigned minsize = MIN(bv1size,bv2size);
|
|
unsigned bytes = minsize/8;
|
|
int result;
|
|
// Compare the full bytes.
|
|
if (bytes) {
|
|
if ((result = memcmp(bv1.begin(),bv2.begin(),bytes))) {return result;}
|
|
}
|
|
// Compare the partial byte, if any.
|
|
unsigned rem = minsize%8;
|
|
if (rem) {
|
|
if ((result = (int) bv1.getField2(bytes,0,rem) - (int) bv2.getField2(bytes,0,rem))) {return result;}
|
|
}
|
|
// All bits the same. The longer guy wins.
|
|
return (int)bv1size - (int)bv2size;
|
|
}
|
|
|
|
// We assume that if the last byte is a partial byte (ie bitsize % 8 != 0)
|
|
// then the remaining unused bits are all equal, should be 0.
|
|
// If they were set with setField, that will be the case.
|
|
bool ByteVector::eql(const ByteVector &other) const
|
|
{
|
|
if (sizeBits() != other.sizeBits()) {return false;} // Quick check to avoid full compare.
|
|
return 0 == compare(*this,other);
|
|
//unsigned bytes = bvsize/8;
|
|
//ByteType *b1 = mStart, *b2 = other.mStart;
|
|
//for (int i = size(); i > 0; i--) { if (*b1++ != *b2++) return false; }
|
|
//return true;
|
|
}
|
|
|
|
#ifdef TEST
|
|
void ByteVectorTest()
|
|
{
|
|
unsigned byten, bitn, l, i;
|
|
const unsigned bvlen = 20;
|
|
ByteVector bv(bvlen), bv2(bvlen), pat(bvlen);
|
|
BitVector bitv(64);
|
|
int printall = 0;
|
|
int tests = 0;
|
|
|
|
ByteVector bctest = ByteVector("12345");
|
|
for (i = 0; i < 5; i++) {
|
|
assert(bctest.getByte(i) == '1'+i);
|
|
}
|
|
|
|
bv.fill(3);
|
|
for (i = 0; i < bvlen; i++) {
|
|
assert(bv.getByte(i) == 3);
|
|
}
|
|
|
|
for (byten = 0; byten <= 1; byten++) {
|
|
for (bitn = 0; bitn <= 7; bitn++) {
|
|
for (l = 1; l <= 33; l++) {
|
|
tests++;
|
|
uint64_t val = 0xffffffffffull & ((1ull<<l)-1);
|
|
// Test setField vs getField.
|
|
bv.fill(3); // Pattern we can check for after the test.
|
|
pat.fill(3);
|
|
bv.setField2(byten,bitn,val,l);
|
|
if (printall) {
|
|
std::cout<<"ok:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)
|
|
<<LOGVAR(val)<<" result="<<bv<<"\n";
|
|
}
|
|
if (bv.getField2(byten,bitn,l) != val) {
|
|
std::cout<<"setField fail:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)
|
|
<<LOGVAR(val)<<" result="<<bv<<"\n";
|
|
break;
|
|
}
|
|
|
|
// Make sure the pattern was not disturbed elsewhere.
|
|
if (byten == 1) { assert(bv.getByte(0) == 3); }
|
|
if (bitn) { assert(bv.getField2(byten,0,bitn) == pat.getField2(byten,0,bitn)); }
|
|
int endbit = byten*8 + bitn + l;
|
|
assert(bv.getField(endbit,30) == pat.getField(endbit,30));
|
|
|
|
// Test getBit vs getField.
|
|
for (int b1=0; b1<3; b1++) {
|
|
for (int k = 0; k <= 7; k++) {
|
|
tests++;
|
|
if (bv.getBit2(b1,k) != bv.getField2(b1,k,1)) {
|
|
std::cout<<"getBit fail:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<"\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test setField vs BitVector::fillField
|
|
bitv.zero();
|
|
bitv.fillField(byten*8+bitn,val,l);
|
|
bitv.pack(bv2.begin());
|
|
bv.fill(0);
|
|
bv.setField(byten*8+bitn,val,l);
|
|
if (bv != bv2) {
|
|
std::cout<<"ByteVector BitVector mismatch:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<"\n";
|
|
std::cout<<"bv="<<bv<<" bv2="<<bv2<<"\n";
|
|
std::cout <<"bitv="<<bitv<<"\n";
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Test fields with large bit counts.
|
|
for (bitn = 1; bitn <= 40; bitn++) {
|
|
for (l = 1; l <= 33; l++) {
|
|
uint64_t val = 0xffffffffffull & ((1ull<<l)-1);
|
|
uint64_t result, expected;
|
|
// Test appendField
|
|
for (int start = 0; start <= 17; start++) { // start bit for append test
|
|
ByteVector bv(20);
|
|
bv.fill(0);
|
|
bv.setAppendP(byten);
|
|
BVASSERT(bv.size() == byten);
|
|
|
|
bv.appendField(0,start); // Move the starting append position.
|
|
if ((result=bv.sizeBits()) != (expected=(byten*8)+start)) {
|
|
std::cout<<"appendField length1 error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
|
|
std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
|
|
}
|
|
|
|
bv.appendField(val,bitn); // This is the test.
|
|
|
|
if ((result=bv.sizeBits()) != (expected=(byten*8)+start+bitn)) {
|
|
std::cout<<"appendField length2 error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
|
|
std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
|
|
}
|
|
if (bv.getField(0,byten*8+start) != 0) {
|
|
std::cout<<"appendField start error\n";
|
|
std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
|
|
}
|
|
if ((result=bv.getField(byten*8+start,bitn)) != (expected=(val & ((1ull<<bitn)-1)))) {
|
|
bv.setAppendP(10); // Needed to read beyond end of our test.
|
|
std::cout<<"appendField value error"<<LOGVAR(expected)<<LOGVAR(result)<<"\n";
|
|
std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
|
|
std::cout<<"bv="<<bv.segmentTemp(0,6)<<"\n";
|
|
}
|
|
if (bv.getField(byten*8+start+bitn,32) != 0) {
|
|
std::cout<<"appendField overrun error\n";
|
|
std::cout<<"at:"<<LOGVAR(byten)<<LOGVAR(bitn)<<LOGVAR(l)<<LOGVAR(val)<<LOGVAR(start)<<"\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
std::cout<<"Finished ByteVector "<<tests<<" tests\n";
|
|
}
|
|
|
|
int main()
|
|
{
|
|
ByteVectorTest();
|
|
}
|
|
#endif // ifdef TEST
|