| counters.h | | counters.h | |
|
| // Copyright 2012 the V8 project authors. All rights reserved. | | // counters.h | |
| // Redistribution and use in source and binary forms, with or without | | /* | |
| // modification, are permitted provided that the following conditions are | | * Copyright (C) 2010 10gen Inc. | |
| // met: | | * | |
| // | | * This program is free software: you can redistribute it and/or modify | |
| // * Redistributions of source code must retain the above copyright | | * it under the terms of the GNU Affero General Public License, version | |
| // notice, this list of conditions and the following disclaimer. | | 3, | |
| // * Redistributions in binary form must reproduce the above | | * as published by the Free Software Foundation. | |
| // copyright notice, this list of conditions and the following | | * | |
| // disclaimer in the documentation and/or other materials provided | | * This program is distributed in the hope that it will be useful, | |
| // with the distribution. | | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| // * Neither the name of Google Inc. nor the names of its | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| // contributors may be used to endorse or promote products derived | | * GNU Affero General Public License for more details. | |
| // from this software without specific prior written permission. | | * | |
| // | | * You should have received a copy of the GNU Affero General Public Lice | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | | nse | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | | */ | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | | | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | | #pragma once | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | | | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | | #include "mongo/pch.h" | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | | #include "../jsobj.h" | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | | #include "../../util/net/message.h" | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | | #include "../../util/processinfo.h" | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | | #include "../../util/concurrency/spin_lock.h" | |
| | | #include "mongo/db/pdfile.h" | |
| #ifndef V8_COUNTERS_H_ | | | |
| #define V8_COUNTERS_H_ | | namespace mongo { | |
| | | | |
| #include "../include/v8.h" | | /** | |
| #include "allocation.h" | | * for storing operation counters | |
| | | * note: not thread safe. ok with that for speed | |
| namespace v8 { | | */ | |
| namespace internal { | | class OpCounters { | |
| | | public: | |
| // StatsCounters is an interface for plugging into external | | | |
| // counters for monitoring. Counters can be looked up and | | OpCounters(); | |
| // manipulated by name. | | void incInsertInWriteLock(int n) { _insert.x += n; } | |
| | | void gotInsert() { _insert++; } | |
| class StatsTable { | | void gotQuery() { _query++; } | |
| public: | | void gotUpdate() { _update++; } | |
| // Register an application-defined function where | | void gotDelete() { _delete++; } | |
| // counters can be looked up. | | void gotGetMore() { _getmore++; } | |
| void SetCounterFunction(CounterLookupCallback f) { | | void gotCommand() { _command++; } | |
| lookup_function_ = f; | | | |
| } | | void gotOp( int op , bool isCommand ); | |
| | | | |
| // Register an application-defined function to create | | BSONObj getObj() const; | |
| // a histogram for passing to the AddHistogramSample function | | | |
| void SetCreateHistogramFunction(CreateHistogramCallback f) { | | // thse are used by snmp, and other things, do not remove | |
| create_histogram_function_ = f; | | const AtomicUInt * getInsert() const { return &_insert; } | |
| } | | const AtomicUInt * getQuery() const { return &_query; } | |
| | | const AtomicUInt * getUpdate() const { return &_update; } | |
| // Register an application-defined function to add a sample | | const AtomicUInt * getDelete() const { return &_delete; } | |
| // to a histogram created with CreateHistogram function | | const AtomicUInt * getGetMore() const { return &_getmore; } | |
| void SetAddHistogramSampleFunction(AddHistogramSampleCallback f) { | | const AtomicUInt * getCommand() const { return &_command; } | |
| add_histogram_sample_function_ = f; | | | |
| } | | private: | |
| | | void _checkWrap(); | |
| bool HasCounterFunction() const { | | | |
| return lookup_function_ != NULL; | | // todo: there will be a lot of cache line contention on these. ne | |
| } | | ed to do something | |
| | | // else eventually. | |
| // Lookup the location of a counter by name. If the lookup | | AtomicUInt _insert; | |
| // is successful, returns a non-NULL pointer for writing the | | AtomicUInt _query; | |
| // value of the counter. Each thread calling this function | | AtomicUInt _update; | |
| // may receive a different location to store it's counter. | | AtomicUInt _delete; | |
| // The return value must not be cached and re-used across | | AtomicUInt _getmore; | |
| // threads, although a single thread is free to cache it. | | AtomicUInt _command; | |
| int* FindLocation(const char* name) { | | }; | |
| if (!lookup_function_) return NULL; | | | |
| return lookup_function_(name); | | extern OpCounters globalOpCounters; | |
| } | | extern OpCounters replOpCounters; | |
| | | | |
| // Create a histogram by name. If the create is successful, | | class NetworkCounter { | |
| // returns a non-NULL pointer for use with AddHistogramSample | | public: | |
| // function. min and max define the expected minimum and maximum | | NetworkCounter() : _bytesIn(0), _bytesOut(0), _requests(0), _overfl | |
| // sample values. buckets is the maximum number of buckets | | ows(0) {} | |
| // that the samples will be grouped into. | | void hit( long long bytesIn , long long bytesOut ); | |
| void* CreateHistogram(const char* name, | | void append( BSONObjBuilder& b ); | |
| int min, | | private: | |
| int max, | | long long _bytesIn; | |
| size_t buckets) { | | long long _bytesOut; | |
| if (!create_histogram_function_) return NULL; | | long long _requests; | |
| return create_histogram_function_(name, min, max, buckets); | | | |
| } | | | |
| | | | |
| // Add a sample to a histogram created with the CreateHistogram | | | |
| // function. | | | |
| void AddHistogramSample(void* histogram, int sample) { | | | |
| if (!add_histogram_sample_function_) return; | | | |
| return add_histogram_sample_function_(histogram, sample); | | | |
| } | | | |
| | | | |
| private: | | | |
| StatsTable(); | | | |
| | | | |
| CounterLookupCallback lookup_function_; | | | |
| CreateHistogramCallback create_histogram_function_; | | | |
| AddHistogramSampleCallback add_histogram_sample_function_; | | | |
| | | | |
| friend class Isolate; | | | |
| | | | |
| DISALLOW_COPY_AND_ASSIGN(StatsTable); | | | |
| }; | | | |
| | | | |
| // StatsCounters are dynamically created values which can be tracked in | | | |
| // the StatsTable. They are designed to be lightweight to create and | | | |
| // easy to use. | | | |
| // | | | |
| // Internally, a counter represents a value in a row of a StatsTable. | | | |
| // The row has a 32bit value for each process/thread in the table and also | | | |
| // a name (stored in the table metadata). Since the storage location can b | | | |
| e | | | |
| // thread-specific, this class cannot be shared across threads. | | | |
| // | | | |
| // This class is designed to be POD initialized. It will be registered wit | | | |
| h | | | |
| // the counter system on first use. For example: | | | |
| // StatsCounter c = { "c:myctr", NULL, false }; | | | |
| struct StatsCounter { | | | |
| const char* name_; | | | |
| int* ptr_; | | | |
| bool lookup_done_; | | | |
| | | | |
| // Sets the counter to a specific value. | | | |
| void Set(int value) { | | | |
| int* loc = GetPtr(); | | | |
| if (loc) *loc = value; | | | |
| } | | | |
| | | | |
| // Increments the counter. | | | |
| void Increment() { | | | |
| int* loc = GetPtr(); | | | |
| if (loc) (*loc)++; | | | |
| } | | | |
| | | | |
| void Increment(int value) { | | | |
| int* loc = GetPtr(); | | | |
| if (loc) | | | |
| (*loc) += value; | | | |
| } | | | |
| | | | |
| // Decrements the counter. | | | |
| void Decrement() { | | | |
| int* loc = GetPtr(); | | | |
| if (loc) (*loc)--; | | | |
| } | | | |
| | | | |
| void Decrement(int value) { | | | |
| int* loc = GetPtr(); | | | |
| if (loc) (*loc) -= value; | | | |
| } | | | |
| | | | |
| // Is this counter enabled? | | | |
| // Returns false if table is full. | | | |
| bool Enabled() { | | | |
| return GetPtr() != NULL; | | | |
| } | | | |
| | | | |
| // Get the internal pointer to the counter. This is used | | | |
| // by the code generator to emit code that manipulates a | | | |
| // given counter without calling the runtime system. | | | |
| int* GetInternalPointer() { | | | |
| int* loc = GetPtr(); | | | |
| ASSERT(loc != NULL); | | | |
| return loc; | | | |
| } | | | |
| | | | |
| protected: | | | |
| // Returns the cached address of this counter location. | | | |
| int* GetPtr() { | | | |
| if (lookup_done_) return ptr_; | | | |
| lookup_done_ = true; | | | |
| ptr_ = FindLocationInStatsTable(); | | | |
| return ptr_; | | | |
| } | | | |
| | | | |
| private: | | | |
| int* FindLocationInStatsTable() const; | | | |
| }; | | | |
| | | | |
| // StatsCounterTimer t = { { L"t:foo", NULL, false }, 0, 0 }; | | | |
| struct StatsCounterTimer { | | | |
| StatsCounter counter_; | | | |
| | | | |
| int64_t start_time_; | | | |
| int64_t stop_time_; | | | |
| | | | |
| // Start the timer. | | | |
| void Start(); | | | |
| | | | |
| // Stop the timer and record the results. | | | |
| void Stop(); | | | |
| | | | |
| // Returns true if the timer is running. | | | |
| bool Running() { | | | |
| return counter_.Enabled() && start_time_ != 0 && stop_time_ == 0; | | | |
| } | | | |
| }; | | | |
| | | | |
| // A Histogram represents a dynamically created histogram in the StatsTable | | | |
| . | | | |
| // | | | |
| // This class is designed to be POD initialized. It will be registered wit | | | |
| h | | | |
| // the histogram system on first use. For example: | | | |
| // Histogram h = { "myhist", 0, 10000, 50, NULL, false }; | | | |
| struct Histogram { | | | |
| const char* name_; | | | |
| int min_; | | | |
| int max_; | | | |
| int num_buckets_; | | | |
| void* histogram_; | | | |
| bool lookup_done_; | | | |
| | | | |
| // Add a single sample to this histogram. | | | |
| void AddSample(int sample); | | | |
| | | | |
| // Returns true if this histogram is enabled. | | | |
| bool Enabled() { | | | |
| return GetHistogram() != NULL; | | | |
| } | | | |
| | | | |
| protected: | | | |
| // Returns the handle to the histogram. | | | |
| void* GetHistogram() { | | | |
| if (!lookup_done_) { | | | |
| lookup_done_ = true; | | | |
| histogram_ = CreateHistogram(); | | | |
| } | | | |
| return histogram_; | | | |
| } | | | |
| | | | |
| private: | | | |
| void* CreateHistogram() const; | | | |
| }; | | | |
| | | | |
| // A HistogramTimer allows distributions of results to be created | | | |
| // HistogramTimer t = { {L"foo", 0, 10000, 50, NULL, false}, 0, 0 }; | | | |
| struct HistogramTimer { | | | |
| Histogram histogram_; | | | |
| | | | |
| int64_t start_time_; | | | |
| int64_t stop_time_; | | | |
| | | | |
| // Start the timer. | | | |
| void Start(); | | | |
| | | | |
| // Stop the timer and record the results. | | | |
| void Stop(); | | | |
| | | | |
| // Returns true if the timer is running. | | | |
| bool Running() { | | | |
| return histogram_.Enabled() && (start_time_ != 0) && (stop_time_ == 0); | | | |
| } | | | |
| }; | | | |
| | | | |
| // Helper class for scoping a HistogramTimer. | | | |
| class HistogramTimerScope BASE_EMBEDDED { | | | |
| public: | | | |
| explicit HistogramTimerScope(HistogramTimer* timer) : | | | |
| timer_(timer) { | | | |
| timer_->Start(); | | | |
| } | | | |
| ~HistogramTimerScope() { | | | |
| timer_->Stop(); | | | |
| } | | | |
| private: | | | |
| HistogramTimer* timer_; | | | |
| }; | | | |
| | | | |
|
| } } // namespace v8::internal | | long long _overflows; | |
| | | | |
|
| #endif // V8_COUNTERS_H_ | | SpinLock _lock; | |
| | | }; | |
| | | | |
| | | extern NetworkCounter networkCounter; | |
| | | } | |
| | | | |
End of changes. 3 change blocks. |
| 273 lines changed or deleted | | 86 lines changed or added | |
|
| dbmessage.h | | dbmessage.h | |
| | | | |
| skipping to change at line 112 | | skipping to change at line 112 | |
| }; | | }; | |
| | | | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| /* For the database/server protocol, these objects and functions encaps
ulate | | /* For the database/server protocol, these objects and functions encaps
ulate | |
| the various messages transmitted over the connection. | | the various messages transmitted over the connection. | |
| | | | |
| See http://dochub.mongodb.org/core/mongowireprotocol | | See http://dochub.mongodb.org/core/mongowireprotocol | |
| */ | | */ | |
| class DbMessage { | | class DbMessage { | |
|
| | | // Assume sizeof(int) == 4 bytes | |
| | | BOOST_STATIC_ASSERT(sizeof(int) == 4); | |
| | | | |
| public: | | public: | |
|
| DbMessage(const Message& _m) : m(_m) , mark(0) { | | // Note: DbMessage constructor reads the first 4 bytes and stores i | |
| // for received messages, Message has only one buffer | | t in reserved | |
| theEnd = _m.singleData()->_data + _m.header()->dataLen(); | | DbMessage(const Message& msg); | |
| char *r = _m.singleData()->_data; | | | |
| reserved = (int *) r; | | // Indicates whether this message is expected to have a ns | |
| data = r + 4; | | // or in the case of dbMsg, a string in the same place as ns | |
| nextjsobj = data; | | bool messageShouldHaveNs() const { | |
| | | return (_msg.operation() >= dbMsg) && (_msg.operation() <= dbDe | |
| | | lete); | |
| } | | } | |
| | | | |
| /** the 32 bit field before the ns | | /** the 32 bit field before the ns | |
| * track all bit usage here as its cross op | | * track all bit usage here as its cross op | |
| * 0: InsertOption_ContinueOnError | | * 0: InsertOption_ContinueOnError | |
| * 1: fromWriteback | | * 1: fromWriteback | |
| */ | | */ | |
|
| int& reservedField() { return *reserved; } | | int reservedField() const { return _reserved; } | |
| | | void setReservedField(int value) { _reserved = value; } | |
| const char * getns() const { | | | |
| return data; | | | |
| } | | | |
| void getns(Namespace& ns) const { | | | |
| ns = data; | | | |
| } | | | |
| | | | |
| const char * afterNS() const { | | | |
| return data + strlen( data ) + 1; | | | |
| } | | | |
| | | | |
|
| int getInt( int num ) const { | | const char * getns() const; | |
| const int * foo = (const int*)afterNS(); | | int getQueryNToReturn() const; | |
| return foo[num]; | | | |
| } | | | |
| | | | |
|
| int getQueryNToReturn() const { | | int getFlags() const; | |
| return getInt( 1 ); | | void setFlags(int value); | |
| } | | | |
| | | | |
|
| /** | | long long getInt64(int offsetBytes) const; | |
| * get an int64 at specified offsetBytes after ns | | | |
| */ | | | |
| long long getInt64( int offsetBytes ) const { | | | |
| const char * x = afterNS(); | | | |
| x += offsetBytes; | | | |
| const long long * ll = (const long long*)x; | | | |
| return ll[0]; | | | |
| } | | | |
| | | | |
|
| void resetPull() { nextjsobj = data; } | | int pullInt(); | |
| int pullInt() const { return pullInt(); } | | long long pullInt64(); | |
| int& pullInt() { | | const long long* getArray(size_t count) const; | |
| if ( nextjsobj == data ) | | | |
| nextjsobj += strlen(data) + 1; // skip namespace | | | |
| int& i = *((int *)nextjsobj); | | | |
| nextjsobj += 4; | | | |
| return i; | | | |
| } | | | |
| long long pullInt64() const { | | | |
| return pullInt64(); | | | |
| } | | | |
| long long &pullInt64() { | | | |
| if ( nextjsobj == data ) | | | |
| nextjsobj += strlen(data) + 1; // skip namespace | | | |
| long long &i = *((long long *)nextjsobj); | | | |
| nextjsobj += 8; | | | |
| return i; | | | |
| } | | | |
| | | | |
| OID* getOID() const { | | | |
| return (OID *) (data + strlen(data) + 1); // skip namespace | | | |
| } | | | |
| | | | |
| void getQueryStuff(const char *&query, int& ntoreturn) { | | | |
| int *i = (int *) (data + strlen(data) + 1); | | | |
| ntoreturn = *i; | | | |
| i++; | | | |
| query = (const char *) i; | | | |
| } | | | |
| | | | |
| /* for insert and update msgs */ | | /* for insert and update msgs */ | |
| bool moreJSObjs() const { | | bool moreJSObjs() const { | |
|
| return nextjsobj != 0; | | return _nextjsobj != 0; | |
| } | | | |
| BSONObj nextJsObj() { | | | |
| if ( nextjsobj == data ) { | | | |
| nextjsobj += strlen(data) + 1; // skip namespace | | | |
| massert( 13066 , "Message contains no documents", theEnd > | | | |
| nextjsobj ); | | | |
| } | | | |
| massert( 10304, | | | |
| "Client Error: Remaining data too small for BSON objec | | | |
| t", | | | |
| theEnd - nextjsobj >= 5 ); | | | |
| | | | |
| if ( cmdLine.objcheck ) { | | | |
| Status status = validateBSON( nextjsobj, theEnd - nextjsobj | | | |
| ); | | | |
| massert( 10307, | | | |
| str::stream() << "Client Error: bad object in mess | | | |
| age: " << status.reason(), | | | |
| status.isOK() ); | | | |
| } | | | |
| | | | |
| BSONObj js(nextjsobj); | | | |
| verify( js.objsize() >= 5 ); | | | |
| verify( js.objsize() < ( theEnd - data ) ); | | | |
| | | | |
| nextjsobj += js.objsize(); | | | |
| if ( nextjsobj >= theEnd ) | | | |
| nextjsobj = 0; | | | |
| return js; | | | |
| } | | } | |
| | | | |
|
| const Message& msg() const { return m; } | | BSONObj nextJsObj(); | |
| | | | |
| | | const Message& msg() const { return _msg; } | |
| | | | |
|
| const char * markGet() { | | const char * markGet() const { | |
| return nextjsobj; | | return _nextjsobj; | |
| } | | } | |
| | | | |
| void markSet() { | | void markSet() { | |
|
| mark = nextjsobj; | | _mark = _nextjsobj; | |
| } | | } | |
| | | | |
|
| void markReset( const char * toMark = 0) { | | void markReset(const char * toMark = NULL); | |
| if( toMark == 0 ) toMark = mark; | | | |
| verify( toMark ); | | | |
| nextjsobj = toMark; | | | |
| } | | | |
| | | | |
| private: | | private: | |
|
| const Message& m; | | // Check if we have enough data to read | |
| int* reserved; | | template<typename T> | |
| const char *data; | | void checkRead(const char* start, size_t count = 0) const; | |
| const char *nextjsobj; | | | |
| const char *theEnd; | | template<typename T> | |
| | | void checkReadOffset(const char* start, size_t offset) const; | |
| | | | |
| | | // Read some type without advancing our position | |
| | | template<typename T> | |
| | | T read() const; | |
| | | | |
| | | // Read some type, and advance our position | |
| | | template<typename T> T readAndAdvance(); | |
| | | | |
| | | const Message& _msg; | |
| | | int _reserved; // flags or zero depending on packet, starts the pac | |
| | | ket | |
| | | | |
| | | const char* _nsStart; // start of namespace string, +4 from message | |
| | | start | |
| | | const char* _nextjsobj; // current position reading packet | |
| | | const char* _theEnd; // end of packet | |
| | | | |
| | | const char* _mark; | |
| | | | |
|
| const char * mark; | | unsigned int _nsLen; | |
| }; | | }; | |
| | | | |
| /* a request to run a query, received from the database */ | | /* a request to run a query, received from the database */ | |
| class QueryMessage { | | class QueryMessage { | |
| public: | | public: | |
| const char *ns; | | const char *ns; | |
| int ntoskip; | | int ntoskip; | |
| int ntoreturn; | | int ntoreturn; | |
| int queryOptions; | | int queryOptions; | |
| BSONObj query; | | BSONObj query; | |
| | | | |
End of changes. 14 change blocks. |
| 110 lines changed or deleted | | 55 lines changed or added | |
|
| message.h | | message.h | |
| | | | |
| skipping to change at line 106 | | skipping to change at line 106 | |
| int messageLength; // total message size, including this | | int messageLength; // total message size, including this | |
| int requestID; // identifier for this message | | int requestID; // identifier for this message | |
| int responseTo; // requestID from the original request | | int responseTo; // requestID from the original request | |
| // (used in responses from db) | | // (used in responses from db) | |
| int opCode; | | int opCode; | |
| }; | | }; | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| #pragma pack(1) | | #pragma pack(1) | |
| /* todo merge this with MSGHEADER (or inherit from it). */ | | /* todo merge this with MSGHEADER (or inherit from it). */ | |
|
| struct MsgData { | | class MsgData { | |
| | | friend class Message; | |
| | | friend class DbMessage; | |
| | | friend class MessagingPort; | |
| | | public: | |
| int len; /* len of the msg, including this field */ | | int len; /* len of the msg, including this field */ | |
| MSGID id; /* request/reply id's match... */ | | MSGID id; /* request/reply id's match... */ | |
| MSGID responseTo; /* id of the message we are responding to */ | | MSGID responseTo; /* id of the message we are responding to */ | |
| short _operation; | | short _operation; | |
| char _flags; | | char _flags; | |
| char _version; | | char _version; | |
|
| | | | |
| int operation() const { | | int operation() const { | |
| return _operation; | | return _operation; | |
| } | | } | |
| void setOperation(int o) { | | void setOperation(int o) { | |
| _flags = 0; | | _flags = 0; | |
| _version = 0; | | _version = 0; | |
| _operation = o; | | _operation = o; | |
| } | | } | |
|
| char _data[4]; | | | |
| | | | |
| int& dataAsInt() { | | int& dataAsInt() { | |
| return *((int *) _data); | | return *((int *) _data); | |
| } | | } | |
| | | | |
| bool valid() { | | bool valid() { | |
| if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) ) | | if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) ) | |
| return false; | | return false; | |
| if ( _operation < 0 || _operation > 30000 ) | | if ( _operation < 0 || _operation > 30000 ) | |
| return false; | | return false; | |
| | | | |
| skipping to change at line 143 | | skipping to change at line 147 | |
| } | | } | |
| | | | |
| long long getCursor() { | | long long getCursor() { | |
| verify( responseTo > 0 ); | | verify( responseTo > 0 ); | |
| verify( _operation == opReply ); | | verify( _operation == opReply ); | |
| long long * l = (long long *)(_data + 4); | | long long * l = (long long *)(_data + 4); | |
| return l[0]; | | return l[0]; | |
| } | | } | |
| | | | |
| int dataLen(); // len without header | | int dataLen(); // len without header | |
|
| | | private: | |
| | | char _data[4]; //must be last member | |
| }; | | }; | |
| const int MsgDataHeaderSize = sizeof(MsgData) - 4; | | const int MsgDataHeaderSize = sizeof(MsgData) - 4; | |
| inline int MsgData::dataLen() { | | inline int MsgData::dataLen() { | |
| return len - MsgDataHeaderSize; | | return len - MsgDataHeaderSize; | |
| } | | } | |
| #pragma pack() | | #pragma pack() | |
| | | | |
| class Message { | | class Message { | |
| public: | | public: | |
| // we assume here that a vector with initial size 0 does no allocat
ion (0 is the default, but wanted to make it explicit). | | // we assume here that a vector with initial size 0 does no allocat
ion (0 is the default, but wanted to make it explicit). | |
| | | | |
End of changes. 4 change blocks. |
| 2 lines changed or deleted | | 8 lines changed or added | |
|