386 lines
18 KiB
C++
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
|