diff --git a/.travis.yml b/.travis.yml index 83aff83fa..ba4ac8bed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -81,7 +81,7 @@ script: CMAKE_OPTS="-DENABLE_UNITTESTS=ON" fi - cmake . -DCMAKE_BUILD_TYPE=$BUILD_TYPE $BUILD_OPTS $CMAKE_OPTS; then echo "Configure OK"; else cat CMakeFiles/CMakeError.log; fi; - - make -j$(nproc); + - make VERBOSE=1 -j$(nproc); - if [ "$TRAVIS_COMPILER" != "x86_64-w64-mingw32-g++" ]; then ulimit -c unlimited; ./test-srt -disable-ipv6; diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 1efabd927..479dff5ed 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -21,6 +20,7 @@ #include "apputil.hpp" #include "netinet_any.h" #include "srt_compat.h" +#include "ofmt.h" using namespace std; using namespace srt; @@ -145,10 +145,10 @@ sockaddr_any CreateAddr(const string& name, unsigned short port, int pref_family string Join(const vector& in, string sep) { - if ( in.empty() ) + if (in.empty()) return ""; - ostringstream os; + srt::ofmtbufstream os; os << in[0]; for (auto i = in.begin()+1; i != in.end(); ++i) diff --git a/apps/logsupport.cpp b/apps/logsupport.cpp index b2ca7772e..0637845cb 100644 --- a/apps/logsupport.cpp +++ b/apps/logsupport.cpp @@ -17,6 +17,7 @@ #include "logsupport.hpp" #include "../srtcore/srt.h" #include "../srtcore/utilities.h" +#include "../srtcore/ofmt.h" using namespace std; using namespace srt; @@ -174,7 +175,7 @@ set SrtParseLogFA(string fa, set* punknown) void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) { - std::ostringstream son, soff; + srt::ofmtbufstream son, soff; for (auto& s: speclist) { diff --git a/apps/srt-file-transmit.cpp b/apps/srt-file-transmit.cpp index f2e53d4a4..a850374a4 100644 --- a/apps/srt-file-transmit.cpp +++ b/apps/srt-file-transmit.cpp @@ -390,8 +390,10 @@ bool DoUpload(UriParser& ut, string path, string filename, while (n > 0) { int st = tar->Write(buf.data() + shift, n, 0, out_stats); - Verb() << "Upload: " << n << " --> " << st - << (!shift ? string() : "+" + Sprint(shift)); + Verb("Upload: ", n, " --> ", st, VerbNoEOL); + if (shift) + Verb("+", shift, VerbNoEOL); + Verb(); if (st == int(SRT_ERROR)) { cerr << "Upload: SRT error: " << srt_getlasterror_str() diff --git a/apps/srt-tunnel.cpp b/apps/srt-tunnel.cpp index ddac21d6e..abb27fefd 100644 --- a/apps/srt-tunnel.cpp +++ b/apps/srt-tunnel.cpp @@ -472,13 +472,13 @@ class SrtMedium: public Medium { string message = srt_strerror(srterror, errnov); - string hm = srt::Sprint("ERROR #", srterror, ": ", text, ": ", message); + string hm = fmtcat("ERROR #", srterror, ": ", text, ": ", message); if (errnov == 0) throw TransmissionError(hm); else { char buf[180]; - throw TransmissionError(srt::Sprint(hm, ": #", errnov, ": ", SysStrError(errnov, buf, 179))); + throw TransmissionError(fmtcat(hm, ": #", errnov, ": ", SysStrError(errnov, buf, 179))); } } diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 025df1229..34f1d0bb1 100644 --- a/apps/transmitmedia.cpp +++ b/apps/transmitmedia.cpp @@ -29,7 +29,11 @@ #include #endif +#define REQUIRE_CXX11 1 + +#include "srt_attr_defs.h" #include "netinet_any.h" +#include "ofmt.h" #include "apputil.hpp" #include "socketoptions.hpp" #include "uriparser.hpp" @@ -230,9 +234,9 @@ void SrtCommon::InitParameters(string host, map par) && transmit_chunk_size > SRT_LIVE_DEF_PLSIZE) { if (transmit_chunk_size > max_payload_size) - Error(Sprint("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); + Error(fmtcat("Chunk size in live mode exceeds ", max_payload_size, " bytes; this is not supported")); - par["payloadsize"] = Sprint(transmit_chunk_size); + par["payloadsize"] = fmts(transmit_chunk_size); } else { @@ -315,7 +319,7 @@ bool SrtCommon::AcceptNewClient() { srt_close(m_bindsock); srt_close(m_sock); - Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + Error(fmtcat("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); } // we do one client connection at a time, @@ -495,7 +499,7 @@ void SrtCommon::ConnectClient(string host, int port) if (m_transtype == SRTT_LIVE && transmit_chunk_size > size_t(maxsize)) { srt_close(m_sock); - Error(Sprint("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); + Error(fmtcat("accepted connection's payload size ", maxsize, " is too small for required ", transmit_chunk_size, " chunk size")); } SRTSTATUS stat = ConfigurePost(m_sock); @@ -586,10 +590,7 @@ SrtCommon::~SrtCommon() SrtSource::SrtSource(string host, int port, const map& par) { Init(host, port, par, false); - - ostringstream os; - os << host << ":" << port; - hostport_copy = os.str(); + hostport_copy = fmtcat(host, ":"_V, port); } int SrtSource::Read(size_t chunk, MediaPacket& pkt, ostream &out_stats) diff --git a/apps/uriparser.cpp b/apps/uriparser.cpp index 7e8be3ae2..45cb4753a 100644 --- a/apps/uriparser.cpp +++ b/apps/uriparser.cpp @@ -57,6 +57,8 @@ UriParser::~UriParser(void) string UriParser::makeUri() { + using namespace srt; + // Reassemble parts into the URI string prefix = ""; if (m_proto != "") @@ -64,37 +66,37 @@ string UriParser::makeUri() prefix = m_proto + "://"; } - std::ostringstream out; + ofmtbufstream out; - out << prefix << m_host; + out.print(prefix, m_host); if ((m_port == "" || m_port == "0") && m_expect == EXPECT_FILE) { // Do not add port } else { - out << ":" << m_port; + out.print(":"_V, m_port); } if (m_path != "") { if (m_path[0] != '/') - out << "/"; - out << m_path; + out.print("/"_V); + out.print(m_path); } if (!m_mapQuery.empty()) { - out << "?"; + out.print("?"_V); query_it i = m_mapQuery.begin(); for (;;) { - out << i->first << "=" << i->second; + out.print(i->first, "="_V, i->second); ++i; if (i == m_mapQuery.end()) break; - out << "&"; + out.print("&"_V); } } diff --git a/apps/verbose.hpp b/apps/verbose.hpp index 56945bf2c..55cf23073 100644 --- a/apps/verbose.hpp +++ b/apps/verbose.hpp @@ -11,7 +11,7 @@ #ifndef INC_SRT_VERBOSE_HPP #define INC_SRT_VERBOSE_HPP -#include +#include "ofmt_iostream.h" #include "sync.h" namespace Verbose diff --git a/srtcore/api.cpp b/srtcore/api.cpp index dba878932..7e069faf7 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -249,7 +249,7 @@ string CUDTUnited::CONID(SRTSOCKET sock) if (int32_t(sock) <= 0) // embraces SRT_INVALID_SOCK, SRT_SOCKID_CONNREQ and illegal negative domain return ""; - std::ostringstream os; + ofmtbufstream os; os << "@" << int(sock) << ":"; return os.str(); } @@ -1732,7 +1732,7 @@ SRTSOCKET CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, for (size_t i = 0; i < g.m_config.size(); ++i) { HLOGC(aclog.Debug, log << "groupConnect: OPTION @" << sid << " #" << g.m_config[i].so); - error_reason = "group-derived option: #" + Sprint(g.m_config[i].so); + error_reason = fmtcat("group-derived option: #", g.m_config[i].so); ns->core().setOpt(g.m_config[i].so, &g.m_config[i].value[0], (int)g.m_config[i].value.size()); } @@ -4003,13 +4003,13 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer& m = i->second; #if ENABLE_HEAVY_LOGGING - ostringstream that_muxer; + ofmtbufstream that_muxer; that_muxer << "id=" << m.id() << " addr=" << m.selfAddr().str(); #endif if (m.selfAddr().hport() == port) { - HLOGC(smlog.Debug, log << "updateListenerMux: reusing muxer: " << that_muxer.str()); + HLOGC(smlog.Debug, log << "updateListenerMux: reusing muxer: " << that_muxer); if (m.selfAddr().family() == s->m_PeerAddr.family()) { mux = &m; // best match @@ -4023,7 +4023,7 @@ bool CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) } else { - HLOGC(smlog.Debug, log << "updateListenerMux: SKIPPING muxer: " << that_muxer.str()); + HLOGC(smlog.Debug, log << "updateListenerMux: SKIPPING muxer: " << that_muxer); } } diff --git a/srtcore/buffer_rcv.cpp b/srtcore/buffer_rcv.cpp index f12b20244..392d26c40 100644 --- a/srtcore/buffer_rcv.cpp +++ b/srtcore/buffer_rcv.cpp @@ -161,7 +161,7 @@ CRcvBuffer::InsertInfo CRcvBuffer::insert(CUnit* unit) IF_RCVBUF_DEBUG(scoped_log.ss << " returns -2"); return InsertInfo(InsertInfo::BELATED); } - IF_HEAVY_LOGGING(string debug_source = "insert %" + Sprint(seqno)); + IF_HEAVY_LOGGING(string debug_source = fmtcat("insert %", seqno)); if (offset >= COff(capacity())) { @@ -510,7 +510,7 @@ std::pair CRcvBuffer::dropUpTo(int32_t seqno) } if (!m_tsbpd.isEnabled() && m_bMessageAPI) updateFirstReadableNonOrder(); - IF_HEAVY_LOGGING(debugShowState(("drop %" + Sprint(seqno)).c_str())); + IF_HEAVY_LOGGING(debugShowState(fmtcat("drop %", seqno).c_str())); return std::make_pair(iNumDropped, iNumDiscarded); } @@ -660,7 +660,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro updateGapInfo(); IF_HEAVY_LOGGING(debugShowState( - ("dropmsg off %" + Sprint(seqnolo) + " #" + Sprint(msgno)).c_str())); + ("dropmsg off %" + fmts(seqnolo) + " #" + fmts(msgno)).c_str())); if (needUpdateNonreadPos) { @@ -674,7 +674,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno, Dro updateFirstReadableNonOrder(); } - IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + Sprint(seqnolo)).c_str())); + IF_HEAVY_LOGGING(debugShowState(("dropmsg off %" + fmts(seqnolo)).c_str())); return iDropCnt; } diff --git a/srtcore/channel.cpp b/srtcore/channel.cpp index 6b346b1fa..380687d9d 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1145,7 +1145,8 @@ EReadStatus CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet) const #endif HLOGC(krlog.Debug, - log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" << hex << msg_flags + log << CONID() << "NET ERROR: packet size=" << recv_size << " msg_flags=0x" + << fmt(msg_flags, hex) << ", detected flags:" << flg.str()); #endif status = RST_AGAIN; diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 383da7fbf..7b6633148 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -282,15 +282,15 @@ void CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const sockaddr } else { - LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << std::hex - << peeraddr16[0] << ":" - << peeraddr16[1] << ":" - << peeraddr16[2] << ":" - << peeraddr16[3] << ":" - << peeraddr16[4] << ":" - << peeraddr16[5] << ":" - << peeraddr16[6] << ":" - << peeraddr16[7] << std::dec); +#if ENABLE_LOGGING + ofmtbufstream peeraddr_form; + fmtc hex04 = fmtc().hex().fillzero().width(4); + peeraddr_form << fmt(peeraddr16[0], hex04); + for (int i = 1; i < 8; ++i) + peeraddr_form << ":" << fmt(peeraddr16[i], hex04); + + LOGC(inlog.Error, log << "pton: IPE or net error: can't determine IPv4 carryover format: " << peeraddr_form); +#endif *target_ipv4_addr = 0; if (peer.family() != AF_INET) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index f145c5f40..fe1270eba 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -96,6 +96,10 @@ using namespace srt_logging; namespace srt { +static inline char fmt_onoff(bool val) { return val ? '+' : '-'; } +// Mark unused because it's only used in HLOGC +SRT_ATR_UNUSED static inline const char* fmt_yesno(bool val) { return val ? "yes" : "no"; } + const size_t SRT_CMD_HSREQ_MINSZ = 8; // Minumum Compatible (1.x.x) packet size (bytes) const size_t SRT_CMD_HSREQ_SZ = 12; // Current version packet size @@ -1820,8 +1824,11 @@ bool CUDT::createSrtHandshake( m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Error, log << CONID() << "createSrtHandshake: IPE: need to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", closing=" << m_bClosing << "."); + << " Socket state: " + << fmt_onoff(m_bConnected) << "connected, " + << fmt_onoff(m_bConnecting) << "connecting, " + << fmt_onoff(m_bBroken) << "broken, " + << fmt_onoff(m_bClosing) << "closing."); return false; } @@ -2151,8 +2158,9 @@ int CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint32_t } LOGC(cnlog.Debug, log << "HSREQ/rcv: cmd=" << SRT_CMD_HSREQ << "(HSREQ) len=" << bytelen - << hex << " vers=0x" << srtdata[SRT_HS_VERSION] << " opts=0x" << srtdata[SRT_HS_FLAGS] - << dec << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); + << " vers=0x" << fmt(srtdata[SRT_HS_VERSION], hex) + << " opts=0x" << fmt(srtdata[SRT_HS_FLAGS], hex) + << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -2386,8 +2394,8 @@ int CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint32_t m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; HLOGC(cnlog.Debug, log << "HSRSP/rcv: Version: " << SrtVersionString(m_uPeerSrtVersion) - << " Flags: SND:" << setw(8) << setfill('0') << hex << m_uPeerSrtFlags - << setw(0) << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); + << " Flags: SND:" << fmt(m_uPeerSrtFlags, fmtc().hex().fillzero().width(8)) + << " (" << SrtFlagString(m_uPeerSrtFlags) << ")"); // Basic version check if (m_uPeerSrtVersion < m_config.uMinimumPeerSrtVersion) { @@ -3223,7 +3231,8 @@ bool CUDT::interpretGroup(CUDTSocket* lsn, const int32_t groupdata[], size_t dat HLOGC(cnlog.Debug, log << CONID() << "interpretGroup: STATE: HsSide=" << hs_side_name[m_SrtHsSide] << " HS MSG: " << MessageTypeStr(UMSG_EXT, hsreq_type_cmd) << " $" << grpid << " type=" << gtp - << " weight=" << link_weight << " flags=0x" << std::hex << link_flags); + << " weight=" << link_weight + << " flags=0x" << fmt(link_flags, hex)); #endif // XXX Here are two separate possibilities: @@ -4063,8 +4072,12 @@ EConnectStatus CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatasize) m_RejectReason = SRT_REJ_IPE; LOGC(cnlog.Error, log << CONID() << "IPE: craftKmResponse needs to send KM, but CryptoControl does not exist." - << " Socket state: connected=" << boolalpha << m_bConnected << ", connecting=" << m_bConnecting - << ", broken=" << m_bBroken << ", opened " << m_bOpened << ", closing=" << m_bClosing << "."); + << " Socket state: " + << fmt_onoff(m_bConnected) << "connected, " + << fmt_onoff(m_bConnecting) << "connecting, " + << fmt_onoff(m_bBroken) << "broken, " + << fmt_onoff(m_bOpened) << "opened, " + << fmt_onoff(m_bClosing) << "closing."); return CONN_REJECT; } // This is a periodic handshake update, so you need to extract the KM data from the @@ -4611,8 +4624,9 @@ EConnectStatus CUDT::processConnectResponse(const CPacket& response, CUDTExcepti if (m_ConnRes.m_iReqType == URQ_INDUCTION) { HLOGC(cnlog.Debug, - log << CONID() << "processConnectResponse: REQ-TIME LOW; got INDUCTION HS response (cookie:" << hex - << m_ConnRes.m_iCookie << " version:" << dec << m_ConnRes.m_iVersion + log << CONID() << "processConnectResponse: REQ-TIME LOW; got INDUCTION HS response (cookie:" + << fmt(m_ConnRes.m_iCookie, hex) + << " version:" << m_ConnRes.m_iVersion << "), sending CONCLUSION HS with this cookie"); m_ConnReq.m_iCookie = m_ConnRes.m_iCookie; @@ -4758,7 +4772,7 @@ EConnectStatus CUDT::postConnect(const CPacket* pResponse, bool rendezvous, CUDT // in rendezvous it's completed before calling this function. if (!rendezvous) { - HLOGC(cnlog.Debug, log << CONID() << boolalpha << "postConnect: packet:" << bool(pResponse) << " rendezvous:" << rendezvous); + HLOGC(cnlog.Debug, log << CONID() << "postConnect: packet:" << fmt_yesno(pResponse) << " rendezvous:" << fmt_yesno(rendezvous)); // The "local storage depleted" case shouldn't happen here, but // this is a theoretical path that needs prevention. bool ok = pResponse; @@ -5474,14 +5488,14 @@ void * CUDT::tsbpd(void* param) HLOGC(tslog.Debug, log << self->CONID() << "tsbpd: DROPSEQ: up to seqno %" << CSeqNo::decseq(info.seqno) << " (" << iDropCnt << " packets) playable at " << FormatTime(info.tsbpd_time) << " delayed " - << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') << (timediff_us % 1000) << " ms"); + << (timediff_us / 1000) << "." << fmt(timediff_us % 1000, fmtc().fixed().fillzero().width(3)) << " ms"); #endif string why; if (self->frequentLogAllowed(FREQLOGFA_RCV_DROPPED, tnow, (why))) { LOGC(brlog.Warn, log << self->CONID() << "RCV-DROPPED " << iDropCnt << " packet(s). Packet seqno %" << info.seqno - << " delayed for " << (timediff_us / 1000) << "." << std::setw(3) << std::setfill('0') - << (timediff_us % 1000) << " ms " << why); + << " delayed for " << (timediff_us / 1000) << "." + << fmt(timediff_us % 1000, fmtc().fixed().fillzero().width(3)) << " ms " << why); } #if SRT_ENABLE_FREQUENT_LOG_TRACE else @@ -6058,12 +6072,12 @@ bool CUDT::frequentLogAllowed(size_t logid, const time_point& tnow, std::string& const int supr = m_aSuppressedMsg[logid]; if (supr > 0) - w_why = Sprint("++SUPPRESSED: ", supr); + w_why = fmtcat("++SUPPRESSED: ", supr); m_aSuppressedMsg[logid] = 0; } else { - w_why = Sprint("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown[logid].load())); + w_why = fmtcat("Too early - last one was ", FormatDuration(tnow - m_tsLogSlowDown[logid].load())); // Set YOUR OWN bit, atomically. m_LogSlowDownExpired |= uint8_t(BIT(logid)); ++m_aSuppressedMsg[logid]; @@ -7829,7 +7843,7 @@ bool CUDT::updateCC(ETransmissionEvent evt, const EventVariant arg) HLOGC(rslog.Debug, log << CONID() << "updateCC: updated values from congctl: interval=" << FormatDuration(m_tdSendInterval) << " (cfg:" << m_CongCtl->pktSndPeriod_us() << "us) cgwindow=" - << std::setprecision(3) << cgwindow); + << fmt(cgwindow, fmtc().precision(7))); #endif } @@ -8496,7 +8510,7 @@ void CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_point // included, but it also triggers for any other kind of invalid value. // This check MUST BE DONE before making any operation on this number. LOGC(inlog.Error, log << CONID() << "ACK: IPE/EPE: received invalid ACK value: " << ackdata_seqno - << " " << std::hex << ackdata_seqno << " (IGNORED)"); + << " " << fmt(ackdata_seqno, hex) << " (IGNORED)"); return; } @@ -9445,7 +9459,7 @@ void CUDT::updateAfterSrtHandshake(int hsv) { SharedLock glock (uglobal().m_GlobControlLock); grpspec = m_parent->m_GroupOf - ? " group=$" + Sprint(m_parent->m_GroupOf->id()) + ? fmtcat(" group=$", m_parent->m_GroupOf->id()) : string(); } #else @@ -10197,12 +10211,11 @@ int CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - std::ostringstream tns1, tns2; + ofmtbufstream buf; // Take the last 2 ciphers from the socket ID. - tns1 << setfill('0') << setw(2) << m_SocketID; - std::string s = tns1.str(); - tns2 << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); - const string thname = tns2.str(); + string s = fmts(m_SocketID, fmtc().fillzero().width(2)); + buf << "SRT:TsbPd:@" << s.substr(s.size()-2, 2); + const string thname = buf.str(); #else const string thname = "SRT:TsbPd"; #endif @@ -11296,7 +11309,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) int32_t cookie_val = bake(addr); - HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << hex << cookie_val); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: new cookie: " << fmt(cookie_val, hex)); // Remember the incoming destination address here and use it as a source // address when responding. It's not possible to record this address yet @@ -11376,7 +11389,7 @@ int CUDT::processConnectRequest(const sockaddr_any& addr, CPacket& packet) if (hs.m_iCookie != cookie_val) { m_RejectReason = SRT_REJ_RDVCOOKIE; - HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << hex << cookie_val << ". Ignoring."); + HLOGC(cnlog.Debug, log << CONID() << "processConnectRequest: ...wrong cookie " << fmt(cookie_val, hex) << ". Ignoring."); return m_RejectReason; } diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index 6ec11e13e..43fbbe0e2 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -533,13 +533,14 @@ void FECFilterBuiltin::ClipPacket(Group& g, const CPacket& pkt) ClipData(g, length_net, kflg, timestamp_hw, pkt.data(), pkt.size()); - HLOGC(pflog.Debug, log << "FEC DATA PKT CLIP: " << hex - << "FLAGS=" << unsigned(kflg) << " LENGTH[ne]=" << (length_net) - << " TS[he]=" << timestamp_hw - << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) - << " LENGTH[ne]=" << g.length_clip - << " TS[he]=" << g.timestamp_clip - << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); + HLOGC(pflog.Debug, log << "FEC DATA PKT CLIP: " + << "FLAGS=" << fmt(kflg, hex) + << " LENGTH[ne]=" << fmt(length_net, hex) + << " TS[he]=" << fmt(timestamp_hw, hex) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, hex) + << " LENGTH[ne]=" << fmt(g.length_clip, hex) + << " TS[he]=" << fmt(g.timestamp_clip, hex) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], hex)); } // Clipping a control packet does merely the same, just the packet has @@ -560,13 +561,14 @@ void FECFilterBuiltin::ClipControlPacket(Group& g, const CPacket& pkt) ClipData(g, *length_clip, *flag_clip, timestamp_hw, payload, payload_clip_len); - HLOGC(pflog.Debug, log << "FEC/CTL CLIP: " << hex - << "FLAGS=" << unsigned(*flag_clip) << " LENGTH[ne]=" << (*length_clip) - << " TS[he]=" << timestamp_hw - << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) - << " LENGTH[ne]=" << g.length_clip - << " TS[he]=" << g.timestamp_clip - << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); + HLOGC(pflog.Debug, log << "FEC/CTL CLIP: " + << "FLAGS=" << fmt(*flag_clip, hex) + << " LENGTH[ne]=" << fmt(*length_clip, hex) + << " TS[he]=" << fmt(timestamp_hw, hex) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, hex) + << " LENGTH[ne]=" << fmt(g.length_clip, hex) + << " TS[he]=" << fmt(g.timestamp_clip, hex) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], hex)); } void FECFilterBuiltin::ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt) @@ -582,13 +584,14 @@ void FECFilterBuiltin::ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt) ClipData(g, length_net, kflg, timestamp_hw, pkt.buffer, pkt.length); - HLOGC(pflog.Debug, log << "FEC REBUILT DATA CLIP: " << hex - << "FLAGS=" << unsigned(kflg) << " LENGTH[ne]=" << (length_net) - << " TS[he]=" << timestamp_hw - << " CLIP STATE: FLAGS=" << unsigned(g.flag_clip) - << " LENGTH[ne]=" << g.length_clip - << " TS[he]=" << g.timestamp_clip - << " PL4=" << (*(uint32_t*)&g.payload_clip[0])); + HLOGC(pflog.Debug, log << "FEC REBUILT DATA CLIP: " + << "FLAGS=" << fmt(kflg, hex) + << " LENGTH[ne]=" << fmt(length_net, hex) + << " TS[he]=" << fmt(timestamp_hw, hex) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, hex) + << " LENGTH[ne]=" << fmt(g.length_clip, hex) + << " TS[he]=" << fmt(g.timestamp_clip, hex) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], hex)); } void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, @@ -765,10 +768,10 @@ void FECFilterBuiltin::PackControl(const Group& g, signed char index, SrtPacket& HLOGC(pflog.Debug, log << "FEC: PackControl: hdr(" << (total_size - g.payload_clip.size()) << "): INDEX=" - << int(index) << " LENGTH[ne]=" << hex << g.length_clip - << " FLAGS=" << int(g.flag_clip) << " TS=" << g.timestamp_clip - << " PL(" << dec << g.payload_clip.size() << ")[0-4]=" << hex - << (*(uint32_t*)&g.payload_clip[0])); + << int(index) << " LENGTH[ne]=" << fmt(g.length_clip, hex) + << " FLAGS=" << fmt(g.flag_clip, hex) << " TS=" << fmt(g.timestamp_clip, hex) + << " PL(" << g.payload_clip.size() << ")[0-4]=" + << fmt(*(uint32_t*)&g.payload_clip[0], hex)); } @@ -1464,7 +1467,7 @@ void FECFilterBuiltin::RcvRebuild(Group& g, int32_t seqno, Group::Type tp) HLOGC(pflog.Debug, log << "FEC: REBUILT: %" << seqno << " msgno=" << MSGNO_SEQ::unwrap(p.hdr[SRT_PH_MSGNO]) << " flags=" << PacketMessageFlagStr(p.hdr[SRT_PH_MSGNO]) - << " TS=" << p.hdr[SRT_PH_TIMESTAMP] << " ID=" << dec << p.hdr[SRT_PH_ID] + << " TS=" << p.hdr[SRT_PH_TIMESTAMP] << " ID=" << p.hdr[SRT_PH_ID] << " size=" << length_hw << " !" << BufferStamp(p.buffer, p.length)); diff --git a/srtcore/group.cpp b/srtcore/group.cpp index fb92ff284..c83fee84d 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -17,6 +17,8 @@ namespace srt { sync::atomic CUDTGroup::s_tokenGen ( 0 ); +static inline char fmt_onoff(bool val) { return val ? '+' : '-'; } + // [[using locked(this->m_GroupLock)]]; bool CUDTGroup::getBufferTimeBase(CUDT* forthesakeof, steady_clock::time_point& w_tb, @@ -2422,8 +2424,8 @@ int CUDTGroup::recv(char* buf, int len, SRT_MSGCTRL& w_mc) if (!m_bOpened || !m_bConnected) { LOGC(grlog.Error, - log << boolalpha << "grp/recv: $" << id() << ": ABANDONING: opened=" << m_bOpened - << " connected=" << m_bConnected); + log << "grp/recv: $" << id() << ": ABANDONING: opened" << fmt_onoff(m_bOpened) + << " connected" << fmt_onoff(m_bConnected)); throw CUDTException(MJ_CONNECTION, MN_NOCONN, 0); } diff --git a/srtcore/logging.cpp b/srtcore/logging.cpp index cc9d14e1d..db63fbb01 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -106,7 +106,7 @@ LogDispatcher::Proxy LogDispatcher::operator()() return Proxy(*this); } -void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) +void LogDispatcher::CreateLogLinePrefix(srt::ofmtbufstream& serr) { using namespace std; using namespace srt; @@ -114,7 +114,7 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) SRT_STATIC_ASSERT(ThreadName::BUFSIZE >= sizeof("hh:mm:ss.") * 2, // multiply 2 for some margin "ThreadName::BUFSIZE is too small to be used for strftime"); char tmp_buf[ThreadName::BUFSIZE]; - if ( !isset(SRT_LOGF_DISABLE_TIME) ) + if (!isset(SRT_LOGF_DISABLE_TIME)) { // Not necessary if sending through the queue. timeval tv; @@ -123,25 +123,22 @@ void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) if (strftime(tmp_buf, sizeof(tmp_buf), "%X.", &tm)) { - serr << tmp_buf << setw(6) << setfill('0') << tv.tv_usec; + serr << tmp_buf << fmt(tv.tv_usec, fmtc().fillzero().width(6)); } } - string out_prefix; - if ( !isset(SRT_LOGF_DISABLE_SEVERITY) ) - { - out_prefix = prefix; - } - // Note: ThreadName::get needs a buffer of size min. ThreadName::BUFSIZE - if ( !isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf) ) + if (!isset(SRT_LOGF_DISABLE_THREADNAME) && ThreadName::get(tmp_buf)) { - serr << "/" << tmp_buf << out_prefix << ": "; + serr << OFMT_RAWSTR("/") << tmp_buf; } - else + + if (!isset(SRT_LOGF_DISABLE_SEVERITY)) { - serr << out_prefix << ": "; + serr.write(prefix, prefix_len); // include terminal 0 } + + serr << OFMT_RAWSTR(": "); } std::string LogDispatcher::Proxy::ExtractName(std::string pretty_function) diff --git a/srtcore/logging.h b/srtcore/logging.h index 0ea0276e3..ceb71a496 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -21,7 +21,6 @@ written by #include #include #include -#include #include #ifdef _WIN32 #include "win/wintime.h" @@ -30,6 +29,7 @@ written by #include #endif +#include "ofmt.h" #include "srt.h" #include "utilities.h" #include "threadname.h" @@ -204,7 +204,7 @@ struct SRT_API LogDispatcher bool CheckEnabled() { return enabled; } - void CreateLogLinePrefix(std::ostringstream&); + void CreateLogLinePrefix(srt::ofmtbufstream&); void SendLogLine(const char* file, int line, const std::string& area, const std::string& sl); // log.Debug("This is the ", nth, " time"); <--- C++11 only. @@ -297,7 +297,7 @@ struct LogDispatcher::Proxy { LogDispatcher& that; - std::ostringstream os; + srt::ofmtbufstream os; // Cache the 'enabled' state in the beginning. If the logging // becomes enabled or disabled in the middle of the log, we don't @@ -348,12 +348,49 @@ struct LogDispatcher::Proxy return *this; } + // Special case for atomics, as passing them to the fmt facility + // requires unpacking the real underlying value. + template + Proxy& operator<<(const srt::sync::atomic& arg) + { + if (that_enabled) + { + os << arg.load(); + } + return *this; + } + +#if HAVE_CXX11 + + void dispatch() {} + + template + void dispatch(const Arg1& a1, const Args&... others) + { + *this << a1; + dispatch(others...); + } + + // Special dispatching for atomics must be provided here. + // By some reason, "*this << a1" expression gets dispatched + // to the general version of operator<<, not the overload for + // atomic. Even though the compiler shows Arg1 type as atomic. + template + void dispatch(const srt::sync::atomic& a1, const Args&... others) + { + *this << a1.load(); + dispatch(others...); + } + +#endif + ~Proxy() { if (that_enabled) { if ((flags & SRT_LOGF_DISABLE_EOL) == 0) - os << std::endl; + os << OFMT_RAWSTR("\n"); // XXX would be nice to use a symbol for it + that.SendLogLine(i_file, i_line, area, os.str()); } // Needed in destructor? @@ -431,28 +468,28 @@ class Logger //extern std::mutex Debug_mutex; -inline void PrintArgs(std::ostream&) {} +inline void PrintArgs(std::ostringstream&) {} template -inline void PrintArgs(std::ostream& serr, Arg1&& arg1, Args&&... args) +inline void PrintArgs(std::ostringstream& serr, Arg1&& arg1, Args&&... args) { serr << std::forward(arg1); PrintArgs(serr, args...); } +// Add exceptional handling for sync::atomic +template +inline void PrintArgs(std::ostringstream& serr, const srt::sync::atomic& arg1, Args&&... args) +{ + serr << arg1.load(); + PrintArgs(serr, args...); +} + template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, Args&&... args SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - std::ostringstream serr; - CreateLogLinePrefix(serr); - PrintArgs(serr, args...); - - if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << std::endl; - - // Not sure, but it wasn't ever used. - SendLogLine(file, line, area, serr.str()); + Proxy(*this).dispatch(args...); #endif } @@ -462,15 +499,7 @@ template inline void LogDispatcher::PrintLogLine(const char* file SRT_ATR_UNUSED, int line SRT_ATR_UNUSED, const std::string& area SRT_ATR_UNUSED, const Arg& arg SRT_ATR_UNUSED) { #ifdef ENABLE_LOGGING - std::ostringstream serr; - CreateLogLinePrefix(serr); - serr << arg; - - if ( !isset(SRT_LOGF_DISABLE_EOL) ) - serr << std::endl; - - // Not sure, but it wasn't ever used. - SendLogLine(file, line, area, serr.str()); + Proxy(*this) << arg; #endif } diff --git a/srtcore/ofmt.h b/srtcore/ofmt.h new file mode 100644 index 000000000..c5aaaed24 --- /dev/null +++ b/srtcore/ofmt.h @@ -0,0 +1,663 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// Formatting library for C++ - C++03 compat version of on-demand tagged format API. +// +// This is a header-only lightweight C++03-compatible formatting library, +// which provides the on-demand tagged format API and iostream-style wrapper +// for FILE type from stdio. It has nothing to do with the rest of the {fmt} +// library, except that it reuses the namespace. + +// USAGE: +// +// 1. Using iostream style: +// +// ofmtbufstream sout; +// +// sout << "Value: " << v << " (" << fmt(v, fmtc().hex().width(2).fillzero()) << ")\n"; +// +// NOTE: When passing a string literal, consider using "Value"_V (C++11 only) +// or OFMT_RAWSTR("Value"). Unfortunately C++ doesn't distinguish "Value" and +// char [20] v = "Value"; both here contain "Value\0", but sizeof(v) for them +// returns the size of the allocated space, not size of the string. Although +// the compiler should expand strlen() in place for literals, note that it +// won't do it if optimizations are turned off. +// +// 2. Using variadic style: +// +// sout.print("Value: ", v, " (", fmt(v, fmtc().hex().width(2).fillzero()), ")\n"); +// +// +// OFMT has also a potential to be used together with iostream, but it requires more +// definition support. This is only the basic fragment to be used with the logging system, +// hence it provides only a wrapper over std::stringstream. + +#ifndef INC_SRT_OFMT_H +#define INC_SRT_OFMT_H + +#include +#include +#include +#include +#include + +#if (defined(__cplusplus) && __cplusplus > 199711L) \ + || (defined(_MSVC_LANG) && _MSVC_LANG > 199711L) // Some earlier versions get this wrong +#define OFMT_HAVE_CXX11 1 +#else +#define OFMT_HAVE_CXX11 0 +#endif + + +namespace srt +{ + +template +struct basic_fmtc +{ +protected: + typedef std::basic_ios ios; + + typedef typename ios::fmtflags fmtflg_t; + fmtflg_t fmtflg; + unsigned short widthval; + unsigned short precisionval; + // Find a way to adjust it to wchar_t if need be + char fillval; + + union + { + struct + { + bool widthbit:1; + bool precisionbit:1; + bool leadzerobit:1; + bool fillbit:1; + } flags; + unsigned char allbits; + }; + + // Mimics the ios::flags, althouh as unsafe it's internal. + void setf(fmtflg_t flags, fmtflg_t mask) + { + fmtflg_t old = fmtflg & ~mask; + fmtflg = old | flags; + } + + void setf(fmtflg_t f) + { + fmtflg |= f; + } + +public: + basic_fmtc(): + fmtflg(fmtflg_t()), + widthval(0), + precisionval(6), + fillval(' '), + allbits(0) + { + } + +#define OFMTC_TAG(name, body) basic_fmtc& name () { body; return *this; } +#define OFMTC_TAG_VAL(name, body) basic_fmtc& name (int val) { body; return *this; } +#define OFMTC_TAG_VAL_TYPE(type, name, body) basic_fmtc& name (type val) { body; return *this; } + + OFMTC_TAG_VAL(width, flags.widthbit = true; widthval = std::abs(val)); + OFMTC_TAG_VAL(precision, flags.precisionbit = true; precisionval = std::abs(val)); + OFMTC_TAG_VAL_TYPE(CharType, fill, flags.fillbit = true; fillval = val); + + OFMTC_TAG(left, setf(ios::left, ios::adjustfield)); + OFMTC_TAG(right, setf(ios::right, ios::adjustfield)); + OFMTC_TAG(internal, setf(ios::internal, ios::adjustfield)); + OFMTC_TAG(dec, setf(ios::dec, ios::basefield)); + OFMTC_TAG(hex, setf(ios::hex, ios::basefield)); + OFMTC_TAG(oct, setf(ios::oct, ios::basefield)); + OFMTC_TAG(uhex, setf(ios::hex, ios::basefield); setf(ios::uppercase)); + OFMTC_TAG(uoct, setf(ios::oct, ios::basefield); setf(ios::uppercase)); + OFMTC_TAG(general, (void)0); + OFMTC_TAG(ugeneral, setf(ios::uppercase)); +#if __cplusplus > 201103L + OFMTC_TAG(fhex, setf(ios::fixed | ios::scientific, ios::floatfield)); + OFMTC_TAG(ufhex, setf(ios::uppercase); setf(ios::fixed | ios::scientific, ios::floatfield)); +#endif + OFMTC_TAG(exp, setf(ios::scientific, ios::floatfield)); + OFMTC_TAG(scientific, setf(ios::scientific, ios::floatfield)); + OFMTC_TAG(uexp, setf(ios::scientific, ios::floatfield); setf(ios::uppercase)); + OFMTC_TAG(uscientific, setf(ios::scientific, ios::floatfield); setf(ios::uppercase)); + OFMTC_TAG(fixed, setf(ios::fixed, ios::floatfield)); + OFMTC_TAG(nopos, (void)0); + OFMTC_TAG(showpos, setf(ios::showpos)); + OFMTC_TAG(showbase, setf(ios::showbase)); + OFMTC_TAG(showpoint, setf(ios::showpoint)); + OFMTC_TAG(fillzero, flags.leadzerobit = true); + +#undef OFMTC_TAG +#undef OFMTC_TAG_VAL +#undef OFMTC_TAG_VAL_TYPE + + void apply(std::basic_ostream& os) const + { + os.flags(fmtflg); + + if (flags.widthbit) + os.width(widthval); + + if (flags.precisionbit) + os.precision(precisionval); + + if (flags.leadzerobit) + { + os.setf(ios::internal, ios::adjustfield); + os.fill(os.widen('0')); + } + else if (flags.fillbit) + { + os.fill(os.widen(fillval)); + } + } +}; + +typedef basic_fmtc fmtc; +typedef basic_fmtc wfmtc; + +// fmt(val, fmtc().alt().hex().width(10)) + +namespace internal +{ +template +struct fmt_proxy +{ + const Value& val; // ERROR: invalidly declared function? --> + // Iostream manipulators should not be sent to the stream. + // use fmt() with fmtc() instead. + basic_fmtc format_spec; + + fmt_proxy(const Value& v, const basic_fmtc& f): val(v), format_spec(f) {} + + template + void sendto(OutStream& os) const + { + std::stringstream tmp; + format_spec.apply(tmp); + tmp << val; + os << tmp.rdbuf(); + } +}; + +template +struct fmt_ios_proxy_1 +{ + const Value& val; // ERROR: invalidly declared function? --> + // Iostream manipulators should not be sent to the stream. + // use fmt() with fmtc() instead. + const Manip& manip; + + fmt_ios_proxy_1(const Value& v, const Manip& m): val(v), manip(m) {} + + template + void sendto(OutStream& os) const + { + std::stringstream tmp; + tmp << manip << val; + os << tmp.rdbuf(); + } +}; + +template +struct fmt_simple_proxy +{ + const Value& val; // ERROR: invalidly declared function? --> + // Iostream manipulators should not be sent to the stream. + // use fmt() with fmtc() instead. + fmt_simple_proxy(const Value& v): val(v) {} + + template + void sendto(OutStream& os) const + { + os << val; + } +}; + +// !!! IMPORTANT !!! +// THIS CLASS IS FOR THE PURPOSE OF DIRECT WRITING TO THE STREAM ONLY. +// DO NOT use this class for any other purpose and use it also with +// EXTREME CARE. +// The only role of this class is to pass the string with KNOWN SIZE +// written in either a string literal or an array of characters to +// the output stream using its `write` method, that is, with bypassing +// any formatting facilities. +struct fmt_stringview +{ +private: + const char* d; + size_t s; + +public: + explicit fmt_stringview(const char* dd, size_t ss): d(dd), s(ss) {} + + const char* data() const { return d; } + size_t size() const { return s; } + + const char* begin() const { return d; } + const char* end() const { return d + s; } +}; + +template +struct check_minus_1 +{ + static const size_t value = N - 1; +}; + +template<> +struct check_minus_1<0> +{ +}; + +// NOTE: DO NOT USE THIS FUNCTION DIRECTLY. +template +inline fmt_stringview CreateRawString_FWD(const char (&ref)[N]) +{ + const char* ptr = ref; + return fmt_stringview(ptr, check_minus_1::value); +} +} + +inline internal::fmt_stringview fmt_rawstr(const char* dd, size_t ss) +{ + return internal::fmt_stringview(dd, ss); +} + +inline internal::fmt_stringview fmt_rawstr(const std::string& s) +{ + return internal::fmt_stringview(s.data(), s.size()); +} + +template inline +internal::fmt_simple_proxy fmt(const Value& val) +{ + return internal::fmt_simple_proxy(val); +} + +template inline +internal::fmt_proxy fmt(const Value& val, const fmtc& config) +{ + return internal::fmt_proxy(val, config); +} + +// A special version that allows using iomanip manipulators +// with the `fmt` call. Currently available only a simple version +// with a single manipulator. The version with multiple manipulators +// requires collecting manipulators in a tuple without knowing their +// types and with sending the tuple elements one by one to the stream +// using operator<<, which isn't trivial in C++11 and requires extra +// facilities to make it possible. Postponed. +template inline +internal::fmt_ios_proxy_1 fmt(const Value& val, const Manip& man) +{ + return internal::fmt_ios_proxy_1(val, man); +} + +// XXX Make basic_ofmtbufstream etc. +class ofmtbufstream +{ + friend class ofmtrefstream; +protected: + std::stringstream buffer; + +public: + ofmtbufstream() {} + + void clear() + { + buffer.clear(); + } + + // Expose + ofmtbufstream& write(const char* buf, size_t size) + { + buffer.write(buf, size); + return *this; + } + + ofmtbufstream& operator<<(const char* t) + { + size_t len = std::strlen(t); + buffer.write(t, len); + return *this; + } + + // Treat a fixed-size array just like a pointer + // to the first only and still use strlen(). This + // is because it usually designates a buffer that + // has N as the spare space, so you still need to + // mind the NUL terminator character. For string + // literals you should use OFMT_RAWSTR macro that + // gets the set of pointer and size from the string + // as an array, but also makes sure that the argument + // is a string literal. + // Unfortunately C++ is unable to distinguish the + // fixed array (with spare buffer space) from a string + // literal (which has only one extra termination character). + // The compiler still can usually call strlen at + // compile time, but not if you are in a debug mode. + template + ofmtbufstream& operator<<(const char (&t)[N]) + { + size_t len = std::strlen(t); + buffer.write(t, len); + return *this; + } + + ofmtbufstream& operator<<(const std::string& s) + { + buffer.write(s.data(), s.size()); + return *this; + } + + // XXX Add also a version for std::string_view, if C++17. + ofmtbufstream& operator<<(const internal::fmt_stringview& s) + { + buffer.write(s.data(), s.size()); + return *this; + } + + template + ofmtbufstream& operator<<(const internal::fmt_simple_proxy& prox) + { + prox.sendto(buffer); + return *this; + } + + template + ofmtbufstream& operator<<(const internal::fmt_proxy& prox) + { + prox.sendto(buffer); + return *this; + } + + template + ofmtbufstream& operator<<(const internal::fmt_ios_proxy_1& prox) + { + prox.sendto(buffer); + return *this; + } + + template inline + ofmtbufstream& operator<<(const Value& val) + { + return *this << fmt(val); + } + + // A utility function to send the argument directly + // to the buffer + template inline + ofmtbufstream& forward(const Value& val) + { + buffer << val; + return *this; + } + + ofmtbufstream& operator<<(const ofmtbufstream& source) + { + buffer << source.buffer.rdbuf(); + return *this; + } + + std::string str() const + { + return buffer.str(); + } + +// Additionally for C++11 +#if OFMT_HAVE_CXX11 + void print_chain() + { + } + + template + void print_chain(const Arg1& arg1, const Args&... args) + { + *this << arg1; + print_chain(args...); + } + + template + ofmtbufstream& print(const Args&... args) + { + print_chain(args...); + return *this; + } + + template + ofmtbufstream& puts(const Args&... args) + { + print_chain(args...); + buffer << std::endl; + return *this; + } +#endif +}; + +class ofmtrefstream +{ +protected: + std::ostream& refstream; + +public: + ofmtrefstream(std::ostream& src) : refstream(src) {} + + // Expose + ofmtrefstream& write(const char* buf, size_t size) + { + refstream.write(buf, size); + return *this; + } + + ofmtrefstream& operator<<(const char* t) + { + size_t len = std::strlen(t); + this->write(t, len); + return *this; + } + + // Treat a fixed-size array just like a pointer + // to the first only and still use strlen(). This + // is because it usually designates a buffer that + // has N as the spare space, so you still need to + // mind the NUL terminator character. For string + // literals you should use OFMT_RAWSTR macro that + // gets the set of pointer and size from the string + // as an array, but also makes sure that the argument + // is a string literal. + // Unfortunately C++ is unable to distinguish the + // fixed array (with spare buffer space) from a string + // literal (which has only one extra termination character). + // The compiler still can usually call strlen at + // compile time, but not if you are in a debug mode. + template + ofmtrefstream& operator<<(const char (&t)[N]) + { + size_t len = std::strlen(t); + this->write(t, len); + return *this; + } + + ofmtrefstream& operator<<(const std::string& s) + { + this->write(s.data(), s.size()); + return *this; + } + + // XXX Add also a version for std::string_view, if C++17. + ofmtrefstream& operator<<(const internal::fmt_stringview& s) + { + this->write(s.data(), s.size()); + return *this; + } + + template + ofmtrefstream& operator<<(const internal::fmt_simple_proxy& prox) + { + prox.sendto(refstream); + return *this; + } + + template + ofmtrefstream& operator<<(const internal::fmt_proxy& prox) + { + prox.sendto(refstream); + return *this; + } + + template + ofmtrefstream& operator<<(const internal::fmt_ios_proxy_1& prox) + { + prox.sendto(refstream); + return *this; + } + + template inline + ofmtrefstream& operator<<(const Value& val) + { + return *this << fmt(val); + } + + // A utility function to send the argument directly + // to the buffer + template inline + ofmtrefstream& forward(const Value& val) + { + refstream << val; + return *this; + } + + ofmtrefstream& operator<<(const ofmtbufstream& source) + { + refstream << source.buffer.rdbuf(); + return *this; + } + +// Additionally for C++11 +#if (defined(__cplusplus) && __cplusplus > 199711L) \ + || (defined(_MSVC_LANG) && _MSVC_LANG > 199711L) // Some earlier versions get this wrong + void print_chain() + { + } + + template + void print_chain(const Arg1& arg1, const Args&... args) + { + *this << arg1; + print_chain(args...); + } + + template + ofmtrefstream& print(const Args&... args) + { + print_chain(args...); + return *this; + } + + template + ofmtrefstream& puts(const Args&... args) + { + print_chain(args...); + refstream << std::endl; + return *this; + } +#endif +}; + +// Additionally for C++11 +#if (defined(__cplusplus) && __cplusplus > 199711L) \ + || (defined(_MSVC_LANG) && _MSVC_LANG > 199711L) // Some earlier versions get this wrong + +inline internal::fmt_stringview operator""_V(const char* ptr, size_t s) +{ + return internal::fmt_stringview(ptr, s); +} + +template inline +std::string fmtcat(const Args&... args) +{ + ofmtbufstream out; + out.print(args...); + return out.str(); +} + +#else + +// Provide fmtcat for C++03 for up to 4 parameters + +// The 1-argument version is for logical consistency. +template inline +std::string fmtcat(const Arg1& arg1) +{ + return fmts(arg1); +} + +template inline +std::string fmtcat(const Arg1& arg1, const Arg2& arg2) +{ + ofmtbufstream out; + out << arg1 << arg2; + return out.str(); +} + +template inline +std::string fmtcat(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3) +{ + ofmtbufstream out; + out << arg1 << arg2 << arg3; + return out.str(); +} + +template inline +std::string fmtcat(const Arg1& arg1, const Arg2& arg2, const Arg3& arg3, const Arg4& arg4) +{ + ofmtbufstream out; + out << arg1 << arg2 << arg3 << arg4; + return out.str(); +} + +#endif + +template inline +std::string fmts(const Value& val) +{ + ofmtbufstream out; + out << val; + return out.str(); +} + +template inline +std::string fmts(const Value& val, const fmtc& fmtspec) +{ + ofmtbufstream out; + out << fmt(val, fmtspec); + return out.str(); +} + + +} + +// This prevents the macro from being used with anything else +// than a string literal. Version of ""_V UDL available for C++03. +#define OFMT_RAWSTR(arg) srt::internal::CreateRawString_FWD("" arg) + + + +#endif diff --git a/srtcore/ofmt_iostream.h b/srtcore/ofmt_iostream.h new file mode 100644 index 000000000..40bf0b937 --- /dev/null +++ b/srtcore/ofmt_iostream.h @@ -0,0 +1,101 @@ +/* + * SRT - Secure, Reliable, Transport + * Copyright (c) 2018 Haivision Systems Inc. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +/***************************************************************************** +written by + Haivision Systems Inc. + *****************************************************************************/ + +// Formatting library for C++ - C++03 compat version of on-demand tagged format API. +// +// This adds the abilities for formatting to be used with iostream. + +#ifndef INC_SRT_OFMT_IOSTREAM_H +#define INC_SRT_OFMT_IOSTREAM_H + +#include +#include +#include +#include "ofmt.h" + +template< + class Value, + class CharT, + class Traits = std::char_traits +> +inline std::basic_ostream& operator<<( + std::basic_ostream& os, + const srt::internal::fmt_proxy& valproxy +) +{ + valproxy.sendto(os); + return os; +} + +template< + class Value, + class CharT, + class Traits = std::char_traits +> +inline std::basic_ostream& operator<<( + std::basic_ostream& os, + const srt::internal::fmt_simple_proxy& valproxy +) +{ + valproxy.sendto(os); + return os; +} + +// Note: if you use iostream and sending to the stream, then +// sending std::string will still use the built-in formatting +// facilities, but you can pass the string through fmt() and +// this way you make a stringview-forwarder and formating gets +// bypassed. +inline std::ostream& operator<<( std::ostream& os, const srt::internal::fmt_stringview& v) +{ + os.write(v.data(), v.size()); + return os; +} + +namespace srt +{ +namespace internal +{ +struct tm_proxy +{ + const struct tm& tim; + const char* format; +}; + +template <> +struct fmt_simple_proxy +{ + const tm_proxy& val; // ERROR: invalidly declared function? --> + // Iostream manipulators should not be sent to the stream. + // use fmt() with fmtc() instead. + fmt_simple_proxy(const tm_proxy& v): val(v) {} + + template + void sendto(OutStream& os) const + { + os << std::put_time(&val.tim, val.format); + } +}; +} + +inline internal::tm_proxy fmt(const struct tm& tim, const char* format) +{ + internal::tm_proxy p = {tim, format}; + return p; +} + +} + +#endif diff --git a/srtcore/queue.cpp b/srtcore/queue.cpp index 210aea13b..674d67cbb 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -573,7 +573,7 @@ void CSndQueue::init(CChannel* c) #if ENABLE_LOGGING ++m_counter; - const string thrname = "SRT:SndQ:w" + Sprint(m_counter); + const string thrname = fmtcat("SRT:SndQ:w", m_counter.load()); const char* thname = thrname.c_str(); #else const char* thname = "SRT:SndQ"; @@ -1228,8 +1228,8 @@ bool CMultiplexer::qualifyToHandleRID(EReadStatus rst, else { HLOGC(cnlog.Debug, - log << "RID: socket @" << i->m_iID << " still active (remaining " << std::fixed - << (count_microseconds(i->m_tsTTL - tsNow) / 1000000.0) << "s of TTL)..."); + log << "RID: socket @" << i->m_iID << " still active (remaining " + << fmt(count_microseconds(i->m_tsTTL - tsNow) / 1000000.0, fixed) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; @@ -1381,7 +1381,7 @@ void CRcvQueue::init(int qsize, size_t payload, CChannel* cc) #if ENABLE_LOGGING const int cnt = ++m_counter; - const string thrname = "SRT:RcvQ:w" + Sprint(cnt); + const string thrname = fmtcat("SRT:RcvQ:w", cnt); #else const string thrname = "SRT:RcvQ:w"; #endif diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index b5acbc35f..f74a373ba 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -758,8 +758,8 @@ struct CSrtConfigSetter { co.uKmPreAnnouncePkt = (km_refresh - 1) / 2; LOGC(aclog.Warn, - log << "SRTO_KMREFRESHRATE=0x" << std::hex << km_refresh << ": setting SRTO_KMPREANNOUNCE=0x" - << std::hex << co.uKmPreAnnouncePkt); + log << "SRTO_KMREFRESHRATE=0x" << fmt(km_refresh, std::hex) << ": setting SRTO_KMPREANNOUNCE=0x" + << fmt(co.uKmPreAnnouncePkt, std::hex)); } } }; @@ -784,7 +784,8 @@ struct CSrtConfigSetter if (km_preanno > (kmref - 1) / 2) { LOGC(aclog.Error, - log << "SRTO_KMPREANNOUNCE=0x" << std::hex << km_preanno << " exceeds KmRefresh/2, 0x" << ((kmref - 1) / 2) + log << "SRTO_KMPREANNOUNCE=0x" << fmt(km_preanno, std::hex) + << " exceeds KmRefresh/2, 0x" << fmt((kmref - 1) / 2, std::hex) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index 420b2c049..2fe47a043 100644 --- a/srtcore/sync.cpp +++ b/srtcore/sync.cpp @@ -50,13 +50,20 @@ std::string FormatTime(const steady_clock::time_point& timestamp) const uint64_t hours = total_sec / (60 * 60) - days * 24; const uint64_t minutes = total_sec / 60 - (days * 24 * 60) - hours * 60; const uint64_t seconds = total_sec - (days * 24 * 60 * 60) - hours * 60 * 60 - minutes * 60; - ostringstream out; + steady_clock::time_point frac = timestamp - seconds_from(total_sec); + ofmtbufstream out; if (days) - out << days << "D "; - out << setfill('0') << setw(2) << hours << ":" - << setfill('0') << setw(2) << minutes << ":" - << setfill('0') << setw(2) << seconds << "." - << setfill('0') << setw(decimals) << (timestamp - seconds_from(total_sec)).time_since_epoch().count() << " [STDY]"; + out << days << OFMT_RAWSTR("D "); + + fmtc d02, dec0; + d02.dec().fillzero().width(2); + dec0.dec().fillzero().width(decimals); + + out << fmt(hours, d02) << OFMT_RAWSTR(":") + << fmt(minutes, d02) << OFMT_RAWSTR(":") + << fmt(seconds, d02) << OFMT_RAWSTR(".") + << fmt(frac.time_since_epoch().count(), dec0) + << OFMT_RAWSTR(" [STDY]"); return out.str(); } @@ -70,10 +77,15 @@ std::string FormatTimeSys(const steady_clock::time_point& timestamp) const time_t tt = now_s + delta_s; struct tm tm = SysLocalTime(tt); // in seconds char tmp_buf[512]; - strftime(tmp_buf, 512, "%X.", &tm); - - ostringstream out; - out << tmp_buf << setfill('0') << setw(6) << (count_microseconds(timestamp.time_since_epoch()) % 1000000) << " [SYST]"; + size_t tmp_size = strftime(tmp_buf, 512, "%X.", &tm); + // Mind the theoretically possible error case + if (!tmp_size) + return "