/* 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 . */ /** @file RLPXFrameWriter.cpp * @author Alex Leverington * @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& 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; }