From 10201568d9c80bb89cbe628c07dd043d72d20b50 Mon Sep 17 00:00:00 2001 From: Amos Jeffries Date: Wed, 4 Mar 2015 08:02:53 -0800 Subject: [PATCH] MemBuf implements Packable interface ... detatch it from Packer wrapper class --- src/HttpHeader.cc | 24 ++++++++---------------- src/HttpMsg.cc | 4 +--- src/HttpReply.cc | 5 +---- src/Makefile.am | 3 +++ src/MemBuf.cc | 12 +----------- src/MemBuf.h | 25 ++++++++----------------- src/Packer.cc | 26 +------------------------- src/Packer.h | 2 +- src/adaptation/ecap/MessageRep.cc | 5 +---- src/adaptation/icap/ModXact.cc | 11 ++--------- src/client_side.cc | 20 +++++--------------- src/errorpage.cc | 8 ++------ src/htcp.cc | 20 +++++++------------- src/http.cc | 4 +--- src/servers/FtpServer.cc | 4 +--- src/tests/stub_MemBuf.cc | 6 ++---- src/tests/testRock.cc | 1 + src/tests/testUfs.cc | 1 + src/tools.cc | 6 ++---- src/tunnel.cc | 4 +--- 20 files changed, 50 insertions(+), 141 deletions(-) diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index 092a69e006..ee556fca63 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -1230,15 +1230,13 @@ HttpHeader::putAuth(const char *auth_scheme, const char *realm) void HttpHeader::putCc(const HttpHdrCc * cc) { - MemBuf mb; - Packer p; assert(cc); /* remove old directives if any */ delById(HDR_CACHE_CONTROL); /* pack into mb */ + MemBuf mb; mb.init(); - packerToMemInit(&p, &mb); - cc->packInto(&p); + cc->packInto(&mb); /* put */ addEntry(new HttpHeaderEntry(HDR_CACHE_CONTROL, NULL, mb.buf)); /* cleanup */ @@ -1248,15 +1246,13 @@ HttpHeader::putCc(const HttpHdrCc * cc) void HttpHeader::putContRange(const HttpHdrContRange * cr) { - MemBuf mb; - Packer p; assert(cr); /* remove old directives if any */ delById(HDR_CONTENT_RANGE); /* pack into mb */ + MemBuf mb; mb.init(); - packerToMemInit(&p, &mb); - httpHdrContRangePackInto(cr, &p); + httpHdrContRangePackInto(cr, &mb); /* put */ addEntry(new HttpHeaderEntry(HDR_CONTENT_RANGE, NULL, mb.buf)); /* cleanup */ @@ -1266,15 +1262,13 @@ HttpHeader::putContRange(const HttpHdrContRange * cr) void HttpHeader::putRange(const HttpHdrRange * range) { - MemBuf mb; - Packer p; assert(range); /* remove old directives if any */ delById(HDR_RANGE); /* pack into mb */ + MemBuf mb; mb.init(); - packerToMemInit(&p, &mb); - range->packInto(&p); + range->packInto(&mb); /* put */ addEntry(new HttpHeaderEntry(HDR_RANGE, NULL, mb.buf)); /* cleanup */ @@ -1284,15 +1278,13 @@ HttpHeader::putRange(const HttpHdrRange * range) void HttpHeader::putSc(HttpHdrSc *sc) { - MemBuf mb; - Packer p; assert(sc); /* remove old directives if any */ delById(HDR_SURROGATE_CONTROL); /* pack into mb */ + MemBuf mb; mb.init(); - packerToMemInit(&p, &mb); - sc->packInto(&p); + sc->packInto(&mb); /* put */ addEntry(new HttpHeaderEntry(HDR_SURROGATE_CONTROL, NULL, mb.buf)); /* cleanup */ diff --git a/src/HttpMsg.cc b/src/HttpMsg.cc index 25907a2db7..231c5c6000 100644 --- a/src/HttpMsg.cc +++ b/src/HttpMsg.cc @@ -335,8 +335,6 @@ void HttpMsg::hdrCacheInit() */ void HttpMsg::firstLineBuf(MemBuf& mb) { - Packer p; - packerToMemInit(&p, &mb); - packFirstLineInto(&p, true); + packFirstLineInto(&mb, true); } diff --git a/src/HttpReply.cc b/src/HttpReply.cc index 2dcd995894..98c20aebf1 100644 --- a/src/HttpReply.cc +++ b/src/HttpReply.cc @@ -127,11 +127,8 @@ MemBuf * HttpReply::pack() { MemBuf *mb = new MemBuf; - Packer p; - mb->init(); - packerToMemInit(&p, mb); - packInto(&p); + packInto(mb); return mb; } diff --git a/src/Makefile.am b/src/Makefile.am index b8d1ea2acb..cf73fe2f99 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3034,6 +3034,7 @@ tests_testString_SOURCES = \ YesNoNone.h \ tests/stub_cache_cf.cc \ tests/stub_cache_manager.cc \ + tests/stub_cbdata.cc \ tests/stub_debug.cc \ tests/stub_HelperChildConfig.cc \ tools.h \ @@ -3693,6 +3694,7 @@ tests_testSBuf_SOURCES= \ tests/stub_libmem.cc \ tests/stub_cache_cf.cc \ tests/stub_cache_manager.cc \ + tests/stub_cbdata.cc \ tests/stub_store.cc \ tests/stub_store_stats.cc \ tests/stub_tools.cc \ @@ -3776,6 +3778,7 @@ tests_testConfigParser_SOURCES = \ YesNoNone.h \ tests/stub_cache_cf.cc \ tests/stub_cache_manager.cc \ + tests/stub_cbdata.cc \ tests/stub_debug.cc \ tests/stub_HelperChildConfig.cc \ tools.h \ diff --git a/src/MemBuf.cc b/src/MemBuf.cc index 73562c8cd1..272adb9b3c 100644 --- a/src/MemBuf.cc +++ b/src/MemBuf.cc @@ -222,7 +222,7 @@ void MemBuf::truncate(mb_size_t tailSize) * calls memcpy, appends exactly size bytes, * extends buffer or creates buffer if needed. */ -void MemBuf::append(const char *newContent, mb_size_t sz) +void MemBuf::append(const char *newContent, int sz) { assert(sz >= 0); assert(buf || (0==capacity && 0==size)); @@ -262,16 +262,6 @@ void MemBuf::terminate() *space() = '\0'; } -/* calls memBufVPrintf */ -void -MemBuf::Printf(const char *fmt,...) -{ - va_list args; - va_start(args, fmt); - vappendf(fmt, args); - va_end(args); -} - /** * vappendf for other printf()'s to use; calls vsnprintf, extends buf if needed */ diff --git a/src/MemBuf.h b/src/MemBuf.h index 020d949897..2049497d24 100644 --- a/src/MemBuf.h +++ b/src/MemBuf.h @@ -9,14 +9,14 @@ #ifndef SQUID_MEMBUF_H #define SQUID_MEMBUF_H +#include "base/Packable.h" #include "cbdata.h" -#include "Packer.h" /** - * Auto-growing memory-resident buffer with printf interface + * Auto-growing memory-resident buffer with Packable interface * \deprecated Use SBuf instead. */ -class MemBuf +class MemBuf : public Packable { CBDATA_CLASS(MemBuf); @@ -28,7 +28,7 @@ public: capacity(0), stolen(0) {} - ~MemBuf() { + virtual ~MemBuf() { if (!stolen && buf) clean(); } @@ -75,7 +75,6 @@ public: void consume(mb_size_t sz); // removes sz bytes, moving content left void consumeWhitespacePrefix(); ///< removes all prefix whitespace, moving content left - void append(const char *c, mb_size_t sz); // grows if needed and possible void appended(mb_size_t sz); // updates content size after external append void truncate(mb_size_t sz); // removes sz last bytes @@ -98,16 +97,6 @@ public: /** unfirtunate hack to test if the buffer has been Init()ialized */ int isNull(); - /** - * calls snprintf, extends buffer if needed - \note we use Printf instead of printf so the compiler won't - * think we're calling the libc printf() - */ - void Printf(const char *fmt,...) PRINTF_FORMAT_ARG2; - - /** vappendf for other printf()'s to use */ - void vappendf(const char *fmt, va_list ap); - /** * freezes the object! and returns function to clear it up. * @@ -115,6 +104,10 @@ public: */ FREE *freeFunc(); + /* Packable API */ + virtual void append(const char *c, int sz); + virtual void vappendf(const char *fmt, va_list ap); + private: /** * private copy constructor and assignment operator generates @@ -161,8 +154,6 @@ public: /** returns free() function to be used, _freezes_ the object! */ void memBufReport(MemBuf * mb); -/** pack content into a mem buf. */ -void packerToMemInit(Packer * p, MemBuf * mb); #endif /* SQUID_MEMBUF_H */ diff --git a/src/Packer.cc b/src/Packer.cc index 54fb615908..55778ccfc9 100644 --- a/src/Packer.cc +++ b/src/Packer.cc @@ -9,7 +9,7 @@ /* DEBUG: section 60 Generic Data Packer */ #include "squid.h" -#include "MemBuf.h" +#include "Packer.h" #include "Store.h" /* @@ -24,18 +24,6 @@ * warning (e.g., "warning: assignment from incompatible pointer type"). */ -static void -memBufAppend(MemBuf *mb, const char *buf, mb_size_t len) -{ - mb->append(buf, len); -} - -static void -memBufVPrintf(MemBuf * mb, const char *fmt, va_list vargs) -{ - mb->vappendf(fmt, vargs); -} - static void storeEntryAppend(StoreEntry *e, const char *buf, int len) { @@ -44,11 +32,9 @@ storeEntryAppend(StoreEntry *e, const char *buf, int len) /* append()'s */ static void (*const store_append) (StoreEntry *, const char *, int) = &storeEntryAppend; -static void (*const memBuf_append) (MemBuf *, const char *, mb_size_t) = &memBufAppend; /* vprintf()'s */ static void (*const store_vprintf) (StoreEntry *, const char *, va_list ap) = &storeAppendVPrintf; -static void (*const memBuf_vprintf) (MemBuf *, const char *, va_list ap) = &memBufVPrintf; /* init/clean */ @@ -63,16 +49,6 @@ packerToStoreInit(Packer * p, StoreEntry * e) e->buffer(); } -/* init with this to accumulate data in MemBuf */ -void -packerToMemInit(Packer * p, MemBuf * mb) -{ - assert(p && mb); - p->append_ = (append_f) memBuf_append; - p->packer_vprintf = (vprintf_f) memBuf_vprintf; - p->real_handler = mb; -} - Packer::~Packer() { if (append_ == (append_f) store_append && real_handler) diff --git a/src/Packer.h b/src/Packer.h index 8dfe8bd285..c8b55eb400 100644 --- a/src/Packer.h +++ b/src/Packer.h @@ -15,7 +15,7 @@ class Packer; /* a common objPackInto interface; used by debugObj */ -typedef void (*ObjPackMethod) (void *obj, Packer * p); +typedef void (*ObjPackMethod) (void *obj, Packable * p); /* append/vprintf's for Packer */ typedef void (*append_f) (void *, const char *buf, int size); diff --git a/src/adaptation/ecap/MessageRep.cc b/src/adaptation/ecap/MessageRep.cc index 8e5feeada1..35b23a5242 100644 --- a/src/adaptation/ecap/MessageRep.cc +++ b/src/adaptation/ecap/MessageRep.cc @@ -91,10 +91,7 @@ Adaptation::Ecap::HeaderRep::image() const { MemBuf mb; mb.init(); - - Packer p; - packerToMemInit(&p, &mb); - theMessage.packInto(&p, true); + theMessage.packInto(&mb, true); return Area::FromTempBuffer(mb.content(), mb.contentSize()); } diff --git a/src/adaptation/icap/ModXact.cc b/src/adaptation/icap/ModXact.cc index dfc72ba4aa..a386ca60dd 100644 --- a/src/adaptation/icap/ModXact.cc +++ b/src/adaptation/icap/ModXact.cc @@ -1317,15 +1317,10 @@ void Adaptation::Icap::ModXact::finalizeLogInfo() } //don't set al.cache.objectSize because it hasn't exist yet - Packer p; MemBuf mb; - mb.init(); - packerToMemInit(&p, &mb); - - reply_->header.packInto(&p); + reply_->header.packInto(&mb); al.headers.reply = xstrdup(mb.buf); - mb.clean(); } prepareLogWithRequestDetails(adapted_request_, alep); @@ -1575,9 +1570,7 @@ void Adaptation::Icap::ModXact::encapsulateHead(MemBuf &icapBuf, const char *sec void Adaptation::Icap::ModXact::packHead(MemBuf &httpBuf, const HttpMsg *head) { - Packer p; - packerToMemInit(&p, &httpBuf); - head->packInto(&p, true); + head->packInto(&httpBuf, true); } // decides whether to offer a preview and calculates its size diff --git a/src/client_side.cc b/src/client_side.cc index a487921634..4bfdde95db 100644 --- a/src/client_side.cc +++ b/src/client_side.cc @@ -520,28 +520,22 @@ prepareLogWithRequestDetails(HttpRequest * request, AccessLogEntry::Pointer &aLo if (Config.onoff.log_mime_hdrs) { MemBuf mb; mb.init(); - Packer pa; - packerToMemInit(&pa, &mb); - request->header.packInto(&pa); + request->header.packInto(&mb); //This is the request after adaptation or redirection aLogEntry->headers.adapted_request = xstrdup(mb.buf); // the virgin request is saved to aLogEntry->request if (aLogEntry->request) { - Packer p; mb.reset(); - packerToMemInit(&p, &mb); - aLogEntry->request->header.packInto(&p); + aLogEntry->request->header.packInto(&mb); aLogEntry->headers.request = xstrdup(mb.buf); } #if USE_ADAPTATION const Adaptation::History::Pointer ah = request->adaptLogHistory(); if (ah != NULL) { - Packer p; mb.reset(); - packerToMemInit(&p, &mb); - ah->lastMeta.packInto(&p); + ah->lastMeta.packInto(&mb); aLogEntry->adapt.last_meta = xstrdup(mb.buf); } #endif @@ -1056,7 +1050,6 @@ static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb) { HttpHeader hdr(hoReply); - Packer p; assert(rep); assert(spec); @@ -1072,14 +1065,11 @@ clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String httpHeaderAddContRange(&hdr, *spec, rep->content_length); - packerToMemInit(&p, mb); - - hdr.packInto(&p); - + hdr.packInto(mb); hdr.clean(); /* append (we packed a header, not a reply) */ - mb->Printf("\r\n"); + mb->append("\r\n", 2); } /** diff --git a/src/errorpage.cc b/src/errorpage.cc index 812464379e..dba17cf379 100644 --- a/src/errorpage.cc +++ b/src/errorpage.cc @@ -737,7 +737,6 @@ ErrorState::Dump(MemBuf * mb) str.Printf("HTTP Request:\r\n"); if (NULL != request) { - Packer pck; String urlpath_or_slash; if (request->urlpath.size() != 0) @@ -750,8 +749,7 @@ ErrorState::Dump(MemBuf * mb) SQUIDSTRINGPRINT(urlpath_or_slash), AnyP::ProtocolType_str[request->http_ver.protocol], request->http_ver.major, request->http_ver.minor); - packerToMemInit(&pck, &str); - request->header.packInto(&pck); + request->header.packInto(&str); } str.Printf("\r\n"); @@ -956,7 +954,6 @@ ErrorState::Convert(char token, bool building_deny_info_url, bool allowRecursion break; } if (NULL != request) { - Packer pck; String urlpath_or_slash; if (request->urlpath.size() != 0) @@ -969,8 +966,7 @@ ErrorState::Convert(char token, bool building_deny_info_url, bool allowRecursion SQUIDSTRINGPRINT(urlpath_or_slash), AnyP::ProtocolType_str[request->http_ver.protocol], request->http_ver.major, request->http_ver.minor); - packerToMemInit(&pck, &mb); - request->header.packInto(&pck, true); //hide authorization data + request->header.packInto(&mb, true); //hide authorization data } else if (request_hdrs) { p = request_hdrs; } else { diff --git a/src/htcp.cc b/src/htcp.cc index ad6d912c36..7a762f9745 100644 --- a/src/htcp.cc +++ b/src/htcp.cc @@ -856,10 +856,6 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad debugs(31, 3, "htcpTstReply: response = " << stuff.response); if (spec) { - MemBuf mb; - mb.init(); - Packer p; - packerToMemInit(&p, &mb); stuff.S.method = spec->method; stuff.S.uri = spec->uri; stuff.S.version = spec->version; @@ -869,7 +865,9 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad hdr.putInt(HDR_AGE, (e->timestamp <= squid_curtime ? (squid_curtime - e->timestamp) : 0) ); else hdr.putInt(HDR_AGE, 0); - hdr.packInto(&p); + MemBuf mb; + mb.init(); + hdr.packInto(&mb); stuff.D.resp_hdrs = xstrdup(mb.buf); stuff.D.respHdrsSz = mb.contentSize(); debugs(31, 3, "htcpTstReply: resp_hdrs = {" << stuff.D.resp_hdrs << "}"); @@ -882,7 +880,7 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad if (e && e->lastmod > -1) hdr.putTime(HDR_LAST_MODIFIED, e->lastmod); - hdr.packInto(&p); + hdr.packInto(&mb); stuff.D.entity_hdrs = xstrdup(mb.buf); stuff.D.entityHdrsSz = mb.contentSize(); @@ -909,7 +907,7 @@ htcpTstReply(htcpDataHeader * dhdr, StoreEntry * e, htcpSpecifier * spec, Ip::Ad } #endif /* USE_ICMP */ - hdr.packInto(&p); + hdr.packInto(&mb); stuff.D.cache_hdrs = xstrdup(mb.buf); stuff.D.cacheHdrsSz = mb.contentSize(); debugs(31, 3, "htcpTstReply: cache_hdrs = {" << stuff.D.cache_hdrs << "}"); @@ -1536,9 +1534,7 @@ htcpQuery(StoreEntry * e, HttpRequest * req, CachePeer * p) HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags); MemBuf mb; mb.init(); - Packer pa; - packerToMemInit(&pa, &mb); - hdr.packInto(&pa); + hdr.packInto(&mb); hdr.clean(); stuff.S.req_hdrs = mb.buf; pktlen = htcpBuildPacket(pkt, sizeof(pkt), &stuff); @@ -1598,9 +1594,7 @@ htcpClear(StoreEntry * e, const char *uri, HttpRequest * req, const HttpRequestM if (reason != HTCP_CLR_INVALIDATION) { HttpStateData::httpBuildRequestHeader(req, e, NULL, &hdr, flags); mb.init(); - Packer pa; - packerToMemInit(&pa, &mb); - hdr.packInto(&pa); + hdr.packInto(&mb); hdr.clean(); stuff.S.req_hdrs = mb.buf; } else { diff --git a/src/http.cc b/src/http.cc index 6c16676a19..e162552f3e 100644 --- a/src/http.cc +++ b/src/http.cc @@ -2184,7 +2184,6 @@ HttpStateData::buildRequestPrefix(MemBuf * mb) /* build and pack headers */ { HttpHeader hdr(hoRequest); - Packer p; httpBuildRequestHeader(request, entry, fwd->al, &hdr, flags); if (request->flags.pinned && request->flags.connectionAuth) @@ -2192,8 +2191,7 @@ HttpStateData::buildRequestPrefix(MemBuf * mb) else if (hdr.has(HDR_AUTHORIZATION)) request->flags.authSent = true; - packerToMemInit(&p, mb); - hdr.packInto(&p); + hdr.packInto(mb); hdr.clean(); } /* append header terminator */ diff --git a/src/servers/FtpServer.cc b/src/servers/FtpServer.cc index b6e0e8cd2f..d852ab748c 100644 --- a/src/servers/FtpServer.cc +++ b/src/servers/FtpServer.cc @@ -1286,10 +1286,8 @@ Ftp::Server::handleRequest(HttpRequest *request) if (do_debug(9, 2)) { MemBuf mb; - Packer p; mb.init(); - packerToMemInit(&p, &mb); - request->pack(&p); + request->pack(&mb); debugs(9, 2, "FTP Client " << clientConnection); debugs(9, 2, "FTP Client REQUEST:\n---------\n" << mb.buf << diff --git a/src/tests/stub_MemBuf.cc b/src/tests/stub_MemBuf.cc index e356f69b0e..37dd2f8c65 100644 --- a/src/tests/stub_MemBuf.cc +++ b/src/tests/stub_MemBuf.cc @@ -15,7 +15,6 @@ mb_size_t MemBuf::spaceSize() const STUB_RETVAL(0) mb_size_t MemBuf::potentialSpaceSize() const STUB_RETVAL(0) void MemBuf::consume(mb_size_t sz) STUB -void MemBuf::append(const char *c, mb_size_t sz) STUB void MemBuf::appended(mb_size_t sz) STUB void MemBuf::truncate(mb_size_t sz) STUB void MemBuf::terminate() STUB @@ -24,10 +23,9 @@ void MemBuf::init() STUB void MemBuf::clean() STUB void MemBuf::reset() STUB int MemBuf::isNull() STUB_RETVAL(1) -void MemBuf::Printf(const char *fmt,...) STUB -void MemBuf::vappendf(const char *fmt, va_list ap) STUB FREE *MemBuf::freeFunc() STUB_RETVAL(NULL) +void MemBuf::append(const char *, int) STUB +void MemBuf::vappendf(const char *fmt, va_list ap) STUB void memBufReport(MemBuf * mb) STUB -void packerToMemInit(Packer * p, MemBuf * mb) STUB diff --git a/src/tests/testRock.cc b/src/tests/testRock.cc index 61ab8962f0..c841c0c913 100644 --- a/src/tests/testRock.cc +++ b/src/tests/testRock.cc @@ -14,6 +14,7 @@ #include "HttpHeader.h" #include "HttpReply.h" #include "MemObject.h" +#include "Packer.h" #include "RequestFlags.h" #include "SquidConfig.h" #include "Store.h" diff --git a/src/tests/testUfs.cc b/src/tests/testUfs.cc index c2e142de62..35698e27e4 100644 --- a/src/tests/testUfs.cc +++ b/src/tests/testUfs.cc @@ -13,6 +13,7 @@ #include "HttpHeader.h" #include "HttpReply.h" #include "MemObject.h" +#include "Packer.h" #include "RequestFlags.h" #include "SquidConfig.h" #include "Store.h" diff --git a/src/tools.cc b/src/tools.cc index a027414aa5..b3b1a7fd7e 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -971,12 +971,10 @@ kb_incr(kb_t * k, size_t v) void debugObj(int section, int level, const char *label, void *obj, ObjPackMethod pm) { - MemBuf mb; - Packer p; assert(label && obj && pm); + MemBuf mb; mb.init(); - packerToMemInit(&p, &mb); - (*pm) (obj, &p); + (*pm) (obj, &mb); debugs(section, level, "" << label << "" << mb.buf << ""); mb.clean(); } diff --git a/src/tunnel.cc b/src/tunnel.cc index 9d6ec5bf61..87f9238194 100644 --- a/src/tunnel.cc +++ b/src/tunnel.cc @@ -1057,7 +1057,6 @@ tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) TunnelStateData *tunnelState = (TunnelStateData *)data; assert(!tunnelState->waitingForConnectExchange()); HttpHeader hdr_out(hoRequest); - Packer p; HttpStateFlags flags; debugs(26, 3, HERE << srv << ", tunnelState=" << tunnelState); memset(&flags, '\0', sizeof(flags)); @@ -1070,8 +1069,7 @@ tunnelRelayConnectRequest(const Comm::ConnectionPointer &srv, void *data) tunnelState->al, /* AccessLogEntry */ &hdr_out, flags); /* flags */ - packerToMemInit(&p, &mb); - hdr_out.packInto(&p); + hdr_out.packInto(&mb); hdr_out.clean(); mb.append("\r\n", 2); -- 2.47.2