mirror of
				https://github.com/RangeNetworks/openbts.git
				synced 2025-11-03 21:33:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			382 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
* Copyright 2011, 2014 Range Networks, Inc.
 | 
						|
*
 | 
						|
* This software is distributed under multiple licenses;
 | 
						|
* see the COPYING file in the main directory for licensing
 | 
						|
* information for this specific distribution.
 | 
						|
*
 | 
						|
* This use of this software may be subject to additional restrictions.
 | 
						|
* See the LEGAL file in the main directory for details.
 | 
						|
 | 
						|
    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.
 | 
						|
*/
 | 
						|
 | 
						|
#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.
 | 
						|
 | 
						|
class ByteVector //: public Vector<ByteType>
 | 
						|
	: public ItemWithSize
 | 
						|
{
 | 
						|
	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.
 | 
						|
	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) { RN_MEMCHKNEW(ByteVector); 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) { RN_MEMCHKNEW(ByteVector) initstr((const char*)wdata,wlen); }
 | 
						|
	ByteVector(const ByteType *wdata) { RN_MEMCHKNEW(ByteVector) initstr((const char*)wdata); }
 | 
						|
	ByteVector(const char *wdata,int wlen) { RN_MEMCHKNEW(ByteVector) initstr(wdata,wlen); }
 | 
						|
	ByteVector(const char *wdata) { RN_MEMCHKNEW(ByteVector) 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) { RN_MEMCHKNEW(ByteVector) dup(other); }
 | 
						|
	ByteVector(const ByteVector&other) : mData(0) { RN_MEMCHKNEW(ByteVector) dup(other); }
 | 
						|
 | 
						|
	ByteVector(const BitVector &other) { RN_MEMCHKNEW(ByteVector) init((other.size()+7)/8); setAppendP(0); append(other); }
 | 
						|
	virtual ~ByteVector() { RN_MEMCHKDEL(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 operator==(const ByteVector &other) const { return eql(other); }
 | 
						|
	bool operator!=(const ByteVector &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 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
 |