diff --git a/apps/apputil.cpp b/apps/apputil.cpp index 22c31521b..c65d6f3d3 100644 --- a/apps/apputil.cpp +++ b/apps/apputil.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -20,6 +19,7 @@ #include "apputil.hpp" #include "netinet_any.h" #include "srt_compat.h" +#include "ofmt.h" using namespace std; using namespace srt; @@ -144,10 +144,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::ofmtstream 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 fbd70c47e..c6217e3c1 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; @@ -173,7 +174,7 @@ set SrtParseLogFA(string fa, set* punknown) void ParseLogFASpec(const vector& speclist, string& w_on, string& w_off) { - std::ostringstream son, soff; + srt::ofmtstream son, soff; for (auto& s: speclist) { diff --git a/apps/transmitmedia.cpp b/apps/transmitmedia.cpp index 275295173..30c602587 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" @@ -37,6 +41,7 @@ #include "srt_compat.h" #include "verbose.hpp" + using namespace std; using namespace srt; @@ -519,10 +524,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 = srt::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 5a5fdf2a1..426ca2c5a 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; + ofmtstream 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/srtcore/api.cpp b/srtcore/api.cpp index bb5dd64fe..7ef2bd9e8 100644 --- a/srtcore/api.cpp +++ b/srtcore/api.cpp @@ -228,7 +228,7 @@ string srt::CUDTUnited::CONID(SRTSOCKET sock) if (sock == 0) return ""; - std::ostringstream os; + ofmtstream os; os << "@" << sock << ":"; return os.str(); } @@ -1403,7 +1403,7 @@ int srt::CUDTUnited::groupConnect(CUDTGroup* pg, SRT_SOCKGROUPCONFIG* targets, i 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 = Sprint("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()); } @@ -3325,14 +3325,14 @@ bool srt::CUDTUnited::updateListenerMux(CUDTSocket* s, const CUDTSocket* ls) CMultiplexer& m = i->second; #if ENABLE_HEAVY_LOGGING - ostringstream that_muxer; + ofmtstream that_muxer; that_muxer << "id=" << m.m_iID << " port=" << m.m_iPort << " ip=" << (m.m_iIPversion == AF_INET ? "v4" : "v6"); #endif if (m.m_iPort == port) { - HLOGC(smlog.Debug, log << "updateListenerMux: reusing muxer: " << that_muxer.str()); + HLOGC(smlog.Debug, log << "updateListenerMux: reusing muxer: " << that_muxer); if (m.m_iIPversion == s->m_PeerAddr.family()) { mux = &m; // best match @@ -3346,7 +3346,7 @@ bool srt::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/channel.cpp b/srtcore/channel.cpp index 9967d7daf..e469e67f0 100644 --- a/srtcore/channel.cpp +++ b/srtcore/channel.cpp @@ -1076,7 +1076,8 @@ srt::EReadStatus srt::CChannel::recvfrom(sockaddr_any& w_addr, CPacket& w_packet #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, fmtc().hex()) << ", detected flags:" << flg.str()); #endif status = RST_AGAIN; diff --git a/srtcore/common.cpp b/srtcore/common.cpp index 2e94668ae..c0ad2a6de 100644 --- a/srtcore/common.cpp +++ b/srtcore/common.cpp @@ -277,15 +277,13 @@ void srt::CIPAddress::pton(sockaddr_any& w_addr, const uint32_t ip[4], const soc } 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); + ofmtstream peeraddr_form; + fmtc hex04; hex04.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); *target_ipv4_addr = 0; if (peer.family() != AF_INET) { diff --git a/srtcore/core.cpp b/srtcore/core.cpp index eca2b2069..e721a3c19 100644 --- a/srtcore/core.cpp +++ b/srtcore/core.cpp @@ -92,6 +92,10 @@ using namespace srt_logging; const SRTSOCKET UDT::INVALID_SOCK = srt::CUDT::INVALID_SOCK; const int UDT::ERROR = srt::CUDT::ERROR; +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"; } + //#define SRT_CMD_HSREQ 1 /* SRT Handshake Request (sender) */ #define SRT_CMD_HSREQ_MINSZ 8 /* Minumum Compatible (1.x.x) packet size (bytes) */ #define SRT_CMD_HSREQ_SZ 12 /* Current version packet size */ @@ -1774,8 +1778,11 @@ bool srt::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; } @@ -2103,8 +2110,9 @@ int srt::CUDT::processSrtMsg_HSREQ(const uint32_t *srtdata, size_t bytelen, uint } 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], fmtc().hex()) + << " opts=0x" << fmt(srtdata[SRT_HS_FLAGS], fmtc().hex()) + << " delay=" << SRT_HS_LATENCY_RCV::unwrap(srtdata[SRT_HS_LATENCY])); m_uPeerSrtVersion = srtdata[SRT_HS_VERSION]; m_uPeerSrtFlags = srtdata[SRT_HS_FLAGS]; @@ -2338,8 +2346,8 @@ int srt::CUDT::processSrtMsg_HSRSP(const uint32_t *srtdata, size_t bytelen, uint 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) { @@ -3141,7 +3149,8 @@ bool srt::CUDT::interpretGroup(const int32_t groupdata[], size_t data_size SRT_A 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, fmtc().hex())); #endif // XXX Here are two separate possibilities: @@ -4102,8 +4111,12 @@ EConnectStatus srt::CUDT::craftKmResponse(uint32_t* aw_kmdata, size_t& w_kmdatas 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 @@ -4650,8 +4663,9 @@ EConnectStatus srt::CUDT::processConnectResponse(const CPacket& response, CUDTEx 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, fmtc().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 srt::CUDT::postConnect(const CPacket* pResponse, bool rendezvous, // 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; @@ -5461,14 +5475,14 @@ void * srt::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 @@ -7772,7 +7786,7 @@ bool srt::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 } @@ -8434,7 +8448,7 @@ void srt::CUDT::processCtrlAck(const CPacket &ctrlpkt, const steady_clock::time_ // 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, fmtc().hex()) << " (IGNORED)"); return; } @@ -9306,7 +9320,7 @@ void srt::CUDT::updateAfterSrtHandshake(int hsv) { ScopedLock glock (uglobal().m_GlobControlLock); grpspec = m_parent->m_GroupOf - ? " group=$" + Sprint(m_parent->m_GroupOf->id()) + ? Sprint(" group=$", m_parent->m_GroupOf->id()) : string(); } #else @@ -10040,12 +10054,11 @@ int srt::CUDT::checkLazySpawnTsbPdThread() HLOGP(qrlog.Debug, "Spawning Socket TSBPD thread"); #if ENABLE_HEAVY_LOGGING - std::ostringstream tns1, tns2; + ofmtstream 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 @@ -11093,7 +11106,7 @@ int srt::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, fmtc().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 @@ -11173,7 +11186,7 @@ int srt::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, fmtc().hex()) << ". Ignoring."); return m_RejectReason; } diff --git a/srtcore/fec.cpp b/srtcore/fec.cpp index fd762b88b..3ef4acb9e 100644 --- a/srtcore/fec.cpp +++ b/srtcore/fec.cpp @@ -533,13 +533,15 @@ 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])); + fmtc x; x.hex(); + HLOGC(pflog.Debug, log << "FEC DATA PKT CLIP: " + << "FLAGS=" << fmt(kflg, x) + << " LENGTH[ne]=" << fmt(length_net, x) + << " TS[he]=" << fmt(timestamp_hw, x) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, x) + << " LENGTH[ne]=" << fmt(g.length_clip, x) + << " TS[he]=" << fmt(g.timestamp_clip, x) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], x)); } // Clipping a control packet does merely the same, just the packet has @@ -560,13 +562,15 @@ 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])); + fmtc x; x.hex(); + HLOGC(pflog.Debug, log << "FEC/CTL CLIP: " + << "FLAGS=" << fmt(*flag_clip, x) + << " LENGTH[ne]=" << fmt(*length_clip, x) + << " TS[he]=" << fmt(timestamp_hw, x) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, x) + << " LENGTH[ne]=" << fmt(g.length_clip, x) + << " TS[he]=" << fmt(g.timestamp_clip, x) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], x)); } void FECFilterBuiltin::ClipRebuiltPacket(Group& g, Receive::PrivPacket& pkt) @@ -582,13 +586,15 @@ 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])); + fmtc x; x.hex(); + HLOGC(pflog.Debug, log << "FEC REBUILT DATA CLIP: " + << "FLAGS=" << fmt(kflg, x) + << " LENGTH[ne]=" << fmt(length_net, x) + << " TS[he]=" << fmt(timestamp_hw, x) + << " CLIP STATE: FLAGS=" << fmt(g.flag_clip, x) + << " LENGTH[ne]=" << fmt(g.length_clip, x) + << " TS[he]=" << fmt(g.timestamp_clip, x) + << " PL4=" << fmt(*(uint32_t*)&g.payload_clip[0], x)); } void FECFilterBuiltin::ClipData(Group& g, uint16_t length_net, uint8_t kflg, @@ -763,12 +769,13 @@ void FECFilterBuiltin::PackControl(const Group& g, signed char index, SrtPacket& pkt.hdr[SRT_PH_TIMESTAMP] = g.timestamp_clip; pkt.hdr[SRT_PH_SEQNO] = seq; + fmtc x; x.hex(); 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, x) + << " FLAGS=" << fmt(g.flag_clip, x) << " TS=" << fmt(g.timestamp_clip, x) + << " PL(" << g.payload_clip.size() << ")[0-4]=" + << fmt(*(uint32_t*)&g.payload_clip[0], x)); } @@ -1464,7 +1471,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 c0df17ac8..ec8c3aafd 100644 --- a/srtcore/group.cpp +++ b/srtcore/group.cpp @@ -15,6 +15,8 @@ extern const int32_t SRT_DEF_VERSION; namespace srt { +static inline char fmt_onoff(bool val) { return val ? '+' : '-'; } + int32_t CUDTGroup::s_tokenGen = 0; // [[using locked(this->m_GroupLock)]]; @@ -2309,8 +2311,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 d0ba3fd4a..08081a083 100644 --- a/srtcore/logging.cpp +++ b/srtcore/logging.cpp @@ -43,7 +43,7 @@ LogDispatcher::Proxy LogDispatcher::operator()() return Proxy(*this); } -void LogDispatcher::CreateLogLinePrefix(std::ostringstream& serr) +void LogDispatcher::CreateLogLinePrefix(srt::ofmtstream& serr) { using namespace std; using namespace srt; @@ -51,7 +51,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; @@ -60,25 +60,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 7782245a2..fe1cb4315 100644 --- a/srtcore/logging.h +++ b/srtcore/logging.h @@ -20,7 +20,6 @@ written by #include #include #include -#include #include #ifdef _WIN32 #include "win/wintime.h" @@ -193,7 +192,7 @@ struct SRT_API LogDispatcher bool CheckEnabled(); - void CreateLogLinePrefix(std::ostringstream&); + void CreateLogLinePrefix(srt::ofmtstream&); 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. @@ -286,7 +285,7 @@ struct LogDispatcher::Proxy { LogDispatcher& that; - std::ostringstream os; + srt::ofmtstream os; // Cache the 'enabled' state in the beginning. If the logging // becomes enabled or disabled in the middle of the log, we don't @@ -337,12 +336,52 @@ struct LogDispatcher::Proxy return *this; } + // Provide explicit overloads for const char* and string + // so that printing them bypasses the formatting facility + + // 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? @@ -438,28 +477,28 @@ inline bool LogDispatcher::CheckEnabled() //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 } @@ -469,15 +508,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..e257e48a1 --- /dev/null +++ b/srtcore/ofmt.h @@ -0,0 +1,386 @@ +// 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. + +#ifndef INC_SRT_IFMT_H +#define INC_SRT_IFMT_H + +#include +#include +#include +#include + +namespace srt +{ + +template +struct basic_fmtc +{ +protected: + // Find a way to adjust it to wchar_t if need be + typedef std::basic_ios ios; + + typedef typename ios::fmtflags fmtflg_t; + fmtflg_t fmtflg; + unsigned short widthval; + unsigned short precisionval; + char fillval; + + union + { + struct + { + bool widthbit:1; + bool precisionbit:1; + bool leadzerobit:1; + bool fillbit:1; + } flags; + uint8_t 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 +{ + 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 +struct fmt_simple_proxy +{ + 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) {} +}; + +// !!! 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: + // This trick is to prevent a possibility to use this class any + // other way than for creating a temporary object. + friend fmt_stringview create_stringview(const char* dd, size_t ss); + + 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; } +}; + +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); +} + +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); +} + + +// XXX Make basic_ofmtstream etc. +class ofmtstream +{ +protected: + std::stringstream buffer; + +public: + + ofmtstream() {} + + void clear() + { + buffer.clear(); + } + + // Expose + ofmtstream& write(const char* buf, size_t size) + { + buffer.write(buf, size); + return *this; + } + + ofmtstream& operator<<(const char* t) + { + size_t len = 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). + template + ofmtstream& operator<<(const char (&t)[N]) + { + size_t len = strlen(t); + buffer.write(t, len); + return *this; + } + + ofmtstream& operator<<(const std::string& s) + { + buffer.write(s.data(), s.size()); + return *this; + } + + ofmtstream& operator<<(const internal::fmt_stringview& s) + { + buffer.write(s.data(), s.size()); + return *this; + } + + template + ofmtstream& operator<<(const internal::fmt_simple_proxy& prox) + { + buffer << prox.val; + return *this; + } + + template + ofmtstream& operator<<(const internal::fmt_proxy& prox) + { + std::stringstream tmp; + prox.format_spec.apply(tmp); + tmp << prox.val; + buffer << tmp.rdbuf(); + return *this; + } + + template inline + ofmtstream& operator<<(const Value& val) + { + return *this << fmt(val); + } + + + ofmtstream& operator<<(const ofmtstream& source) + { + buffer << source.buffer.rdbuf(); + return *this; + } + + std::string str() const + { + return buffer.str(); + } + +// 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 + ofmtstream& print(const Args&... args) + { + print_chain(args...); + return *this; + } + + template + ofmtstream& puts(const Args&... args) + { + print_chain(args...); + buffer << 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) +{ + ofmtstream out; + out.print(args...); + return out.str(); +} + +#endif + +template inline +std::string fmts(const Value& val) +{ + ofmtstream out; + out << val; + return out.str(); +} + +template inline +std::string fmts(const Value& val, const fmtc& fmtspec) +{ + ofmtstream 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/queue.cpp b/srtcore/queue.cpp index 6cb4faeb1..7e836501f 100644 --- a/srtcore/queue.cpp +++ b/srtcore/queue.cpp @@ -455,7 +455,7 @@ void srt::CSndQueue::init(CChannel* c, CTimer* t) #if ENABLE_LOGGING ++m_counter; - const std::string thrname = "SRT:SndQ:w" + Sprint(m_counter); + const std::string thrname = Sprint("SRT:SndQ:w", m_counter.load()); const char* thname = thrname.c_str(); #else const char* thname = "SRT:SndQ"; @@ -1106,8 +1106,8 @@ bool srt::CRendezvousQueue::qualifyToHandle(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, fmtc().fixed()) << "s of TTL)..."); } const steady_clock::time_point tsLastReq = i->m_pUDT->m_tsLastReqTime; @@ -1217,7 +1217,7 @@ void srt::CRcvQueue::init(int qsize, size_t payload, int version, int hsize, CCh #if ENABLE_LOGGING const int cnt = ++m_counter; - const std::string thrname = "SRT:RcvQ:w" + Sprint(cnt); + const std::string thrname = Sprint("SRT:RcvQ:w", cnt); #else const std::string thrname = "SRT:RcvQ:w"; #endif diff --git a/srtcore/socketconfig.cpp b/srtcore/socketconfig.cpp index 1c067d059..b74245d7e 100644 --- a/srtcore/socketconfig.cpp +++ b/srtcore/socketconfig.cpp @@ -735,8 +735,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, fmtc().hex()) << ": setting SRTO_KMPREANNOUNCE=0x" + << fmt(co.uKmPreAnnouncePkt, fmtc().hex())); } } }; @@ -761,7 +761,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, fmtc().hex()) + << " exceeds KmRefresh/2, 0x" << fmt((kmref - 1) / 2, fmtc().hex()) << " - OPTION REJECTED."); throw CUDTException(MJ_NOTSUP, MN_INVAL, 0); } diff --git a/srtcore/sync.cpp b/srtcore/sync.cpp index bfe153657..b44a54d72 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); + ofmtstream 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 << srt::fmt(hours, d02) << OFMT_RAWSTR(":") + << srt::fmt(minutes, d02) << OFMT_RAWSTR(":") + << srt::fmt(seconds, d02) << OFMT_RAWSTR(".") + << srt::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 "