OpenBTS-UMTS/CommonLibs/ByteVector.h

386 lines
18 KiB
C++

/*
* OpenBTS provides an open source alternative to legacy telco protocols and
* traditionally complex, proprietary hardware systems.
*
* Copyright 2011-2014 Range Networks, Inc.
*
* This software is distributed under the terms of the GNU Affero General
* Public License version 3. See the COPYING and NOTICE files in the main
* directory for licensing information.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*/
#ifndef _BYTEVECTOR_H_
#define _BYTEVECTOR_H_
#include <stdint.h>
#include <arpa/inet.h>
#include "MemoryLeak.h"
#include "BitVector.h"
#include "ScalarTypes.h"
#include "Logger.h"
// Originally based on BitVector, based on Vector
// ByteVector is like a Vector but for objects of type... guess what?
// ByteVector also has an efficient append facility.
// A ByteVector consists of packed memory that is byte-aligned on the left side
// and bit-aligned on the right side. Both the left and right side can be moved
// back and forth within the constraints of the originally allocated memory.
// See: trimLeft, trimRight, growLeft, and the many append functions.
// The basic strategy is that ByteVectors are always allocated initially
// such that size() is the full allocated size, so if you want to use the append
// feature you must call setAppendP() (or conceivably trimRight) to set the location
// where you want to start appending.
// Exceeding the edges of the allocated memory area throws ByteVectorError
// There are two classes defined here:
// o ByteVector points to a memory area that it manages.
// All segments derived from a ByteVector share the same memory using refcnts.
// When the last segment is deleted, the memory is freed.
// o ByteVectorTemp is identical but does not 'own' the memory, rather it points into
// an area of memory that it does not manage. It allows you to use the rather extensive
// set of ByteVector manipulation functions on some other memory, or derive a segment
// using segmentTemp when you know for sure that the derived segment is temporary and
// will not outlive the original ByteVector.
// It is unwise to expand the left or right side of a ByteVectorTemp because there is
// no protection for exceeding the bounds of the memory area, however those functions
// are not currently over-ridden in ByteVectorTemp to remove them, but they should be eliminated.
// ByteVector is the base class and can refer to either ByteVectors that own memory,
// or ByteVectorTemps that do not.
// I started inheriting from Vector but we could only reuse a few lines of code
// from there so it is not worth the trouble. It would be better to push
// the appending ability down into Vector. But appending creates a different
// concept of size() than Vector which would be a major change.
// To avoid confusion with Vector type functions resize() is not defined in ByteVector.
#define NEW_SEGMENT_SEMANTICS 1
#define BYTEVECTOR_REFCNT 1 // ByteVectors now use reference counting
#define BVASSERT(expr) {if (!(expr)) throw ByteVectorError();}
class ByteVectorError {};
typedef uint8_t ByteType;
void sethtonl(ByteType *cp,unsigned value);
void sethtons(ByteType *cp,unsigned value);
uint16_t getntohs(ByteType *cp);
uint32_t getntohl(ByteType *cp);
class ItemWithSize {
virtual size_t sizeBits() const =0;
virtual size_t sizeBytes() const =0;
};
class ByteVectorTemp;
class Dorky {}; // Used to force use of a particular constructor.
DEFINE_MEMORY_LEAK_DETECTOR_CLASS(ByteVector,MemCheckByteVector)
class ByteVector : public ItemWithSize, public MemCheckByteVector
{
ByteType* mData; ///< allocated data block, if any
ByteType* mStart; ///< start of useful data, always >=mData and <=mAllocEnd
unsigned mSizeBits; ///< size of useful data in bits.
ByteType *mAllocEnd; ///< end of allocated data + 1.
//unsigned mBitInd;
unsigned bitind() { return mSizeBits % 8; }
#if BYTEVECTOR_REFCNT
// The first mDataOffset bytes of mData is a short reference count of the number
// of ByteVectors pointing at it.
static const int mDataOffset = sizeof(short);
int setRefCnt(int val) { return ((short*)mData)[0] = val; }
int decRefCnt() { return setRefCnt(((short*)mData)[0] - 1); }
void incRefCnt() { setRefCnt(((short*)mData)[0] + 1); }
#endif
void init(size_t newSize); /** set size and allocated size to that specified */
void initstr(const char *wdata, unsigned wlen) { init(wlen); setAppendP(0); append(wdata,wlen); }
void initstr(const char *wdata) { initstr(wdata,strlen(wdata)); }
void dup(const ByteVector& other);
// Constructor: A ByteVector that does not own any memory, but is just a segment
// of some other memory. This constructor is private.
// The public way to do this is to use segmentTemp or create a ByteVectorTemp.
protected:
ByteVector(Dorky,ByteType*wstart,ByteType*wend)
: mData(0), mStart(wstart), mSizeBits(8*(wend-wstart)), mAllocEnd(wend) {}
//: mData(0), mStart(wstart), mEnd(wend), mAllocEnd(wend), mBitInd(0) {}
public:
void clear(); // Release the memory used by this ByteVector.
// clone semantics are weird: copies data from other to self.
void clone(const ByteVector& other); /** Copy data from another vector. */
#if BYTEVECTOR_REFCNT
int getRefCnt() { return mData ? ((short*)mData)[0] : 0; }
#endif
const ByteType* begin() const { return mStart; }
ByteType*begin() { return mStart; }
// This is the allocSize from mStart, not from mData, ie, excluding the refcnt.
size_t allocSize() const { return mAllocEnd - mStart; }
//size_t sizeBytes() const { return mEnd - mStart + !!mBitInd; } // size in bytes
size_t sizeBytes() const { return (mSizeBits+7)/8; } // size in bytes
size_t size() const { return sizeBytes(); } // size in bytes
//size_t sizeBits() const { return size() * 8 + mBitInd; } // size in bits
size_t sizeBits() const { return mSizeBits; } // size in bits
size_t sizeRemaining() const { // Remaining full bytes for appends
return (mAllocEnd - mStart) - sizeBytes();
//int result = mAllocEnd - mEnd - !!mBitInd;
//return result >= 0 ? (size_t) result : 0;
}
void resetSize() { mSizeBits = 8*allocSize(); }
// Set the Write Position for appends. This is equivalent to setting size().
void setAppendP(size_t bytep, unsigned bitp=0) {
BVASSERT(bytep + !!bitp <= allocSize());
mSizeBits = bytep*8 + bitp;
//mEnd=mStart+bytep; mBitInd=bitp;
}
void setSizeBits(size_t bits) {
BVASSERT((bits+7)/8 <= allocSize());
mSizeBits = bits;
//mEnd=mStart+(bits/8); mBitInd=bits%8;
}
// Concat unimplemented.
//ByteVector(const ByteVector& source1, const ByteVector source2);
/** Constructor: An empty Vector of a given size. */
ByteVector(size_t wSize=0) { init(wSize); }
// Constructor: A ByteVector whose contents is from a string with optional length specified.
// A copy of the string is malloced into the ByteVector.
// ByteType is "unsigned char" so we need both signed and unsigned versions to passify C++.
ByteVector(const ByteType *wdata,int wlen) { initstr((const char*)wdata,wlen); }
ByteVector(const ByteType *wdata) { initstr((const char*)wdata); }
ByteVector(const char *wdata,int wlen) { initstr(wdata,wlen); }
ByteVector(const char *wdata) { initstr(wdata); }
// Constructor: A ByteVector which is a duplicate of some other.
// They both 'own' the memory using refcounts.
// The const is tricky - other is modified despite the 'const', because only the ByteVector itself is 'const',
// the memory it points to is not.
// See also: alias.
ByteVector(ByteVector& other) : mData(0) { dup(other); }
ByteVector(const ByteVector&other) : mData(0) { dup(other); }
ByteVector(const BitVector &other) { init((other.size()+7)/8); setAppendP(0); append(other); }
virtual ~ByteVector() { clear(); }
// Make a duplicate of the vector where both point into shared memory, and increment the refcnt.
// Use clone if you want a completely distinct copy.
void operator=(ByteVector& other) { dup(other); }
// The BitVector class implements this with a clone().
// However, this gets called if a hidden intermediary variable is required
// to implement the assignment, for example: x = segment(...);
// In this case it is safer for the class to call clone to be safe,
// however, that is probably never what is wanted by the calling code,
// so I am leaving it out of here. Only use this type of assignment if the
// other does not own the memory.
// Update: With refcnts, these issues evaporate, and the two forms
// of assignment are identical.
void operator=(const ByteVector& other) {
#if BYTEVECTOR_REFCNT
dup(other);
#else
BVASSERT(other.mData == NULL); // Dont use assignment in this case.
set(other);
#endif
}
static int compare(const ByteVector &bv1, const ByteVector &bv2);
bool eql(const ByteVector &other) const;
bool eql(const std::string &other) const;
bool operator==(const ByteVector &other) const { return eql(other); }
bool operator==(const std::string &other) const { return eql(other); }
bool operator!=(const ByteVector &other) const { return !eql(other); }
bool operator!=(const std::string &other) const { return !eql(other); }
// This is here so you put ByteVectors in a map, which needs operator< defined.
bool operator<(const ByteVector &other) const { return compare(*this,other)<0; }
bool operator>(const ByteVector &other) const { return compare(*this,other)>0; }
bool operator<=(const ByteVector &other) const { return compare(*this,other)<=0; }
bool operator>=(const ByteVector &other) const { return compare(*this,other)>=0; }
/**@name Functions identical to Vector: */
// Return a segment of a ByteVector that shares the same memory as the original.
// The const is tricky - the original ByteVector itself is not modified, but the memory it points to is.
ByteVector segment(size_t start, size_t span) const;
// For the const version, since we are not allowed to modify the original
// to change the refcnt, we have to either return a segment that does not own memory,
// or a completely new piece of memory. So I am using a different name so that
// it is obvious that the memory ownership is being stripped off the ByteVector.
const ByteVectorTemp segmentTemp(size_t start, size_t span) const;
// Copy other to this starting at start.
// The 'this' ByteVector must be allocated large enough to hold other.
void setSegment(size_t start, ByteVector&other);
bool isOwner() { return !!mData; } // Do we own any memory ourselves?
// Trim specified number of bytes from left or right in place.
// growLeft is the opposite: move the mStart backward by amt, throw error if no room.
// New space is uninitialized.
// These are for byte-aligned only, so we assert bit index is 0.
void trimLeft(unsigned amt);
void trimRight(unsigned amt);
ByteType *growLeft(unsigned amt);
void growRight(unsigned amt);
void shiftLeftBits(int amtbits);
void fill(ByteType byte, size_t start, size_t span);
void fill(ByteType byte, size_t start) { fill(byte,start,size()-start); }
void fill(ByteType byte) { fill(byte,0,size()); }
void appendFill(ByteType byte, size_t span);
// Zero out the rest of the ByteVector:
void appendZero() {
if (bitind()) appendField(0,8-bitind()); // 0 fill partial byte.
appendFill(0,allocSize() - size()); // 0 fill remaining bytes.
}
// 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.
void copyToSegment(ByteVector& other, size_t start, size_t span) const;
/** Copy all of this Vector to a segment of another Vector. */
void copyToSegment(ByteVector& other, size_t start=0) const;
// pat 2-2012: I am modifying this to use the refcnts, so to get
// a segment with an incremented refcnt, use: alias().segment(...)
//ByteVector alias() { return segment(0,size()); }
ByteVector head(size_t span) const { return segment(0,span); }
//const ByteVector headTemp(size_t span) const { return segmentTemp(0,span); }
ByteVector tail(size_t start) const { return segment(start,size()-start); }
//const ByteVector tailTemp(size_t start) const { return segmentTemp(start,size()-start); }
// 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.
// Note that this family of functions all assume byte-aligned fields.
// See setField/appendField for bit-aligned fields.
unsigned grow(unsigned amt);
unsigned growBits(unsigned amt);
void setByte(size_t ind, ByteType byte) { BVASSERT(ind < size()); mStart[ind] = byte; }
void setUInt16(size_t writeIndex,unsigned value); // 2 byte value
void setUInt32(size_t writeIndex, unsigned value); // 4 byte value
void appendByte(unsigned value) { BVASSERT(bitind()==0); setByte(grow(1),value); }
void appendUInt16(unsigned value) { BVASSERT(bitind()==0); setUInt16(grow(2),value); }
void appendUInt32(unsigned value) { BVASSERT(bitind()==0); setUInt32(grow(4),value); }
ByteType getByte(size_t ind) const { BVASSERT(ind < size()); return mStart[ind]; }
ByteType getNibble(size_t ind,int hi) const {
ByteType val = getByte(ind); return hi ? (val>>4) : val & 0xf;
}
unsigned getUInt16(size_t readIndex) const; // 2 byte value
unsigned getUInt32(size_t readIndex) const; // 4 byte value
ByteType readByte(size_t &rp) { return getByte(rp++); }
unsigned readUInt16(size_t &rp);
unsigned readUInt32(size_t &rp);
unsigned readLI(size_t &rp); // GSM8.16 1 or 2 octet length indicator.
void append(const ByteVector&other);
void append(const BitVector&other);
void append(const ByteType*bytes, unsigned len);
void append(const char*bytes, unsigned len) { append((const ByteType*)bytes,len); }
void appendLI(unsigned len); // GSM8.16 1 or 2 octet length indicator.
void append(const ByteVector*other) { append(*other); }
void append(const BitVector*other) { append(*other); }
// Set from another ByteVector.
// The other is typically a segment which does not own the memory, ie:
// v1.set(v2.segment()) The other is not a reference because
// the result of segment() is not a variable to which
// the reference operator can be applied.
// This is not really needed any more because the refcnts take care of these cases.
void set(ByteVector other) // That's right. No ampersand.
{
#if BYTEVECTOR_REFCNT
// Its ok, everything will work.
dup(other);
#else
BVASSERT(other.mData == NULL); // Dont use set() in this case.
clear();
mStart=other.mStart; mEnd=other.mEnd; mAllocEnd = other.mAllocEnd; mBitInd = other.mBitInd;
#endif
}
// Bit aligned operations.
// The "2" suffix versions specify both byte and bit index in the range 0..7.
// The "R1" suffix versions are identical to the "2" suffix versions,
// but with start bit numbered from 1 like this: 8 | 7 | ... | 1
// This is just a convenience because all the specs number the bits this weird way.
// The no suffix versions take a bit pos ranging from 0 to 8*size()-1
// Get a bit from the specified byte, numbered like this: 0 | 1 | ... | 7
bool getBit2(size_t byteIndex, unsigned bitIndex) const;
// Get a bit in same bit order, but with start bit numbered from 1 like this: 8 | 7 | ... | 1
// Many GSM L3 specs specify numbering this way.
bool getBitR1(size_t byteIndex, unsigned bitIndex) const {
return getBit2(byteIndex,8-bitIndex);
}
bool getBit(unsigned bitPos) const { return getBit2(bitPos/8,bitPos%8); }
void setBit2(size_t byteIndex, unsigned bitIndex, unsigned val);
void setBit(unsigned bitPos, unsigned val) { setBit2(bitPos/8,bitPos%8,val); }
// Set/get fields giving both byte and bit index.
void setField2(size_t byteIndex, size_t bitIndex, uint64_t value,unsigned lengthBits);
uint64_t getField2(size_t byteIndex, size_t bitIndex, unsigned lengthBits) const;
uint64_t getFieldR1(size_t byteIndex, size_t bitIndex, unsigned length) const {
return getField2(byteIndex,8-bitIndex,length);
}
// Set/get bit field giving bit position treating the entire ByteVector as a string of bits.
void setField(size_t bitPos, uint64_t value, unsigned lengthBits) {
setField2(bitPos/8,bitPos%8,value,lengthBits);
}
uint64_t getField(size_t bitPos, unsigned lengthBits) const { // aka peekField
return getField2(bitPos/8,bitPos%8,lengthBits);
}
// Identical to getField, but add lengthBits to readIndex.
uint64_t readField(size_t& readIndex, unsigned lengthBits) const {
uint64_t result = getField(readIndex,lengthBits);
readIndex += lengthBits;
return result;
}
void appendField(uint64_t value,unsigned lengthBits);
// This works for Field<> data types.
void appendField(ItemWithValueAndWidth &item) {
appendField(item.getValue(),item.getWidth());
}
std::string str() const;
std::string hexstr() const;
};
class ByteVectorTemp : public ByteVector
{
public:
ByteVectorTemp(ByteType*wstart,ByteType*wend) : ByteVector(Dorky(),wstart,wend) {}
ByteVectorTemp(size_t) { assert(0); }
// Constructor: A ByteVector whose contents is from a string with optional length specified.
// These cannot be const because the ByteVectorTemp points into the original memory
// and has methods to modify it.
// ByteType is "unsigned char" so we need both signed and unsigned versions to passify C++.
// All the explicit casts are required. This is so brain dead.
ByteVectorTemp(ByteType *wdata,int wlen) : ByteVector(Dorky(),wdata,wdata+wlen) {}
ByteVectorTemp(ByteType *wdata) : ByteVector(Dorky(),wdata,wdata+strlen((char*)wdata)) {}
ByteVectorTemp(char *wdata,int wlen) : ByteVector(Dorky(),(ByteType*)wdata,(ByteType*)wdata+wlen) {}
ByteVectorTemp(char *wdata) : ByteVector(Dorky(),(ByteType*)wdata,(ByteType*)wdata+strlen((char*)wdata)) {}
ByteVectorTemp(ByteVector& other) : ByteVector(Dorky(),other.begin(),other.begin()+other.size()) {}
ByteVectorTemp(BitVector &) { assert(0); }
};
// Warning: C++ prefers an operator<< that is const to one that is not.
std::ostream& operator<<(std::ostream&os, const ByteVector&vec);
#endif