You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

161 lines
4.9 KiB

/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameWriter.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "RLPXFrameWriter.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
const uint16_t RLPXFrameWriter::EmptyFrameLength = h128::size * 3; // header + headerMAC + frameMAC
const uint16_t RLPXFrameWriter::MinFrameDequeLength = h128::size * 4; // header + headerMAC + padded-block + frameMAC
void RLPXFrameWriter::enque(RLPXPacket&& _p, PacketPriority _priority)
{
if (!_p.isValid())
return;
WriterState& qs = _priority ? m_q.first : m_q.second;
DEV_GUARDED(qs.x)
qs.q.push_back(move(_p));
}
void RLPXFrameWriter::enque(uint8_t _packetType, RLPStream& _payload, PacketPriority _priority)
{
enque(RLPXPacket(m_protocolType, (RLPStream() << _packetType), _payload), _priority);
}
size_t RLPXFrameWriter::mux(RLPXFrameCoder& _coder, unsigned _size, vector<bytes>& o_toWrite)
{
static const size_t c_blockSize = h128::size;
static const size_t c_overhead = c_blockSize * 3; // header + headerMac + frameMAC
if (_size < c_overhead + c_blockSize)
return 0;
size_t ret = 0;
size_t frameLen = _size / 16 * 16;
bytes payload(0);
bool swapQueues = false;
while (frameLen >= c_overhead + c_blockSize)
{
bool highPending;
bool lowPending;
DEV_GUARDED(m_q.first.x)
highPending = !!m_q.first.q.size();
DEV_GUARDED(m_q.second.x)
lowPending = !!m_q.second.q.size();
if (!highPending && !lowPending)
return ret;
// first run when !swapQueues, high > low, otherwise low > high
bool high = highPending && !swapQueues ? true : !lowPending;
WriterState &qs = high ? m_q.first : m_q.second;
size_t frameAllot = (!swapQueues && highPending && lowPending ? frameLen / 2 - (c_overhead + c_blockSize) > 0 ? frameLen / 2 : frameLen : frameLen) - c_overhead;
size_t offset = 0;
size_t length = 0;
while (frameAllot >= c_blockSize)
{
// Invariants:
// !qs.writing && payload.empty() initial entry
// !qs.writing && !payload.empty() continuation (multiple packets per frame)
// qs.writing && payload.empty() initial entry, continuation (multiple frames per packet)
// qs.writing && !payload.empty() INVALID
// write packet-type
if (qs.writing == nullptr)
{
{
Guard l(qs.x);
if (!qs.q.empty())
qs.writing = &qs.q[0];
else
break;
}
// break here if we can't write-out packet-type
// or payload is packed and next packet won't fit (implicit)
qs.multiFrame = qs.writing->size() > frameAllot;
assert(qs.writing->type().size());
if (qs.writing->type().size() > frameAllot || (qs.multiFrame && !payload.empty()))
{
qs.writing = nullptr;
qs.remaining = 0;
qs.multiFrame = false;
break;
}
else if (qs.multiFrame)
qs.sequence = ++m_sequenceId;
frameAllot -= qs.writing->type().size();
payload += qs.writing->type();
qs.remaining = qs.writing->data().size();
}
// write payload w/remaining allotment
assert(qs.multiFrame || (!qs.multiFrame && frameAllot >= qs.remaining));
if (frameAllot && qs.remaining)
{
offset = qs.writing->data().size() - qs.remaining;
length = qs.remaining <= frameAllot ? qs.remaining : frameAllot;
bytes portion = bytesConstRef(&qs.writing->data()).cropped(offset, length).toBytes();
qs.remaining -= length;
frameAllot -= portion.size();
payload += portion;
}
assert((!qs.remaining && (offset > 0 || !qs.multiFrame)) || (qs.remaining && qs.multiFrame));
if (!qs.remaining)
{
qs.writing = nullptr;
DEV_GUARDED(qs.x)
qs.q.pop_front();
ret++;
}
// qs.writing is left alone for first frame of multi-frame packet
if (qs.multiFrame)
break;
}
if (!payload.empty())
{
if (qs.multiFrame)
if (offset == 0)
// 1st frame of segmented packet writes total-size of packet
_coder.writeFrame(m_protocolType, qs.sequence, qs.writing->size(), &payload, payload);
else
_coder.writeFrame(m_protocolType, qs.sequence, &payload, payload);
else
_coder.writeFrame(m_protocolType, &payload, payload);
assert(frameLen >= payload.size());
frameLen -= payload.size();
o_toWrite.push_back(payload);
payload.resize(0);
if (!qs.remaining && qs.multiFrame)
qs.multiFrame = false;
}
else if (swapQueues)
break;
swapQueues = true;
}
return ret;
}