OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
(...skipping 201 matching lines...) Loading... |
212 | 212 |
213 PseudoTcp::PseudoTcp(IPseudoTcpNotify* notify, uint32_t conv) | 213 PseudoTcp::PseudoTcp(IPseudoTcpNotify* notify, uint32_t conv) |
214 : m_notify(notify), | 214 : m_notify(notify), |
215 m_shutdown(SD_NONE), | 215 m_shutdown(SD_NONE), |
216 m_error(0), | 216 m_error(0), |
217 m_rbuf_len(DEFAULT_RCV_BUF_SIZE), | 217 m_rbuf_len(DEFAULT_RCV_BUF_SIZE), |
218 m_rbuf(m_rbuf_len), | 218 m_rbuf(m_rbuf_len), |
219 m_sbuf_len(DEFAULT_SND_BUF_SIZE), | 219 m_sbuf_len(DEFAULT_SND_BUF_SIZE), |
220 m_sbuf(m_sbuf_len) { | 220 m_sbuf(m_sbuf_len) { |
221 // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic) | 221 // Sanity check on buffer sizes (needed for OnTcpWriteable notification logic) |
222 ASSERT(m_rbuf_len + MIN_PACKET < m_sbuf_len); | 222 RTC_DCHECK(m_rbuf_len + MIN_PACKET < m_sbuf_len); |
223 | 223 |
224 uint32_t now = Now(); | 224 uint32_t now = Now(); |
225 | 225 |
226 m_state = TCP_LISTEN; | 226 m_state = TCP_LISTEN; |
227 m_conv = conv; | 227 m_conv = conv; |
228 m_rcv_wnd = m_rbuf_len; | 228 m_rcv_wnd = m_rbuf_len; |
229 m_rwnd_scale = m_swnd_scale = 0; | 229 m_rwnd_scale = m_swnd_scale = 0; |
230 m_snd_nxt = 0; | 230 m_snd_nxt = 0; |
231 m_snd_wnd = 1; | 231 m_snd_wnd = 1; |
232 m_snd_una = m_rcv_nxt = 0; | 232 m_snd_una = m_rcv_nxt = 0; |
233 m_bReadEnable = true; | 233 m_bReadEnable = true; |
234 m_bWriteEnable = false; | 234 m_bWriteEnable = false; |
235 m_t_ack = 0; | 235 m_t_ack = 0; |
236 | 236 |
237 m_msslevel = 0; | 237 m_msslevel = 0; |
238 m_largest = 0; | 238 m_largest = 0; |
239 ASSERT(MIN_PACKET > PACKET_OVERHEAD); | 239 RTC_DCHECK(MIN_PACKET > PACKET_OVERHEAD); |
240 m_mss = MIN_PACKET - PACKET_OVERHEAD; | 240 m_mss = MIN_PACKET - PACKET_OVERHEAD; |
241 m_mtu_advise = MAX_PACKET; | 241 m_mtu_advise = MAX_PACKET; |
242 | 242 |
243 m_rto_base = 0; | 243 m_rto_base = 0; |
244 | 244 |
245 m_cwnd = 2 * m_mss; | 245 m_cwnd = 2 * m_mss; |
246 m_ssthresh = m_rbuf_len; | 246 m_ssthresh = m_rbuf_len; |
247 m_lastrecv = m_lastsend = m_lasttraffic = now; | 247 m_lastrecv = m_lastsend = m_lasttraffic = now; |
248 m_bOutgoing = false; | 248 m_bOutgoing = false; |
249 | 249 |
(...skipping 131 matching lines...) Loading... |
381 } else { | 381 } else { |
382 RTC_NOTREACHED(); | 382 RTC_NOTREACHED(); |
383 } | 383 } |
384 } | 384 } |
385 void PseudoTcp::SetOption(Option opt, int value) { | 385 void PseudoTcp::SetOption(Option opt, int value) { |
386 if (opt == OPT_NODELAY) { | 386 if (opt == OPT_NODELAY) { |
387 m_use_nagling = value == 0; | 387 m_use_nagling = value == 0; |
388 } else if (opt == OPT_ACKDELAY) { | 388 } else if (opt == OPT_ACKDELAY) { |
389 m_ack_delay = value; | 389 m_ack_delay = value; |
390 } else if (opt == OPT_SNDBUF) { | 390 } else if (opt == OPT_SNDBUF) { |
391 ASSERT(m_state == TCP_LISTEN); | 391 RTC_DCHECK(m_state == TCP_LISTEN); |
392 resizeSendBuffer(value); | 392 resizeSendBuffer(value); |
393 } else if (opt == OPT_RCVBUF) { | 393 } else if (opt == OPT_RCVBUF) { |
394 ASSERT(m_state == TCP_LISTEN); | 394 RTC_DCHECK(m_state == TCP_LISTEN); |
395 resizeReceiveBuffer(value); | 395 resizeReceiveBuffer(value); |
396 } else { | 396 } else { |
397 RTC_NOTREACHED(); | 397 RTC_NOTREACHED(); |
398 } | 398 } |
399 } | 399 } |
400 | 400 |
401 uint32_t PseudoTcp::GetCongestionWindow() const { | 401 uint32_t PseudoTcp::GetCongestionWindow() const { |
402 return m_cwnd; | 402 return m_cwnd; |
403 } | 403 } |
404 | 404 |
(...skipping 23 matching lines...) Loading... |
428 | 428 |
429 size_t read = 0; | 429 size_t read = 0; |
430 rtc::StreamResult result = m_rbuf.Read(buffer, len, &read, NULL); | 430 rtc::StreamResult result = m_rbuf.Read(buffer, len, &read, NULL); |
431 | 431 |
432 // If there's no data in |m_rbuf|. | 432 // If there's no data in |m_rbuf|. |
433 if (result == rtc::SR_BLOCK) { | 433 if (result == rtc::SR_BLOCK) { |
434 m_bReadEnable = true; | 434 m_bReadEnable = true; |
435 m_error = EWOULDBLOCK; | 435 m_error = EWOULDBLOCK; |
436 return SOCKET_ERROR; | 436 return SOCKET_ERROR; |
437 } | 437 } |
438 ASSERT(result == rtc::SR_SUCCESS); | 438 RTC_DCHECK(result == rtc::SR_SUCCESS); |
439 | 439 |
440 size_t available_space = 0; | 440 size_t available_space = 0; |
441 m_rbuf.GetWriteRemaining(&available_space); | 441 m_rbuf.GetWriteRemaining(&available_space); |
442 | 442 |
443 if (uint32_t(available_space) - m_rcv_wnd >= | 443 if (uint32_t(available_space) - m_rcv_wnd >= |
444 std::min<uint32_t>(m_rbuf_len / 2, m_mss)) { | 444 std::min<uint32_t>(m_rbuf_len / 2, m_mss)) { |
445 // TODO(jbeda): !?! Not sure about this was closed business | 445 // TODO(jbeda): !?! Not sure about this was closed business |
446 bool bWasClosed = (m_rcv_wnd == 0); | 446 bool bWasClosed = (m_rcv_wnd == 0); |
447 m_rcv_wnd = static_cast<uint32_t>(available_space); | 447 m_rcv_wnd = static_cast<uint32_t>(available_space); |
448 | 448 |
(...skipping 36 matching lines...) Loading... |
485 | 485 |
486 // | 486 // |
487 // Internal Implementation | 487 // Internal Implementation |
488 // | 488 // |
489 | 489 |
490 uint32_t PseudoTcp::queue(const char* data, uint32_t len, bool bCtrl) { | 490 uint32_t PseudoTcp::queue(const char* data, uint32_t len, bool bCtrl) { |
491 size_t available_space = 0; | 491 size_t available_space = 0; |
492 m_sbuf.GetWriteRemaining(&available_space); | 492 m_sbuf.GetWriteRemaining(&available_space); |
493 | 493 |
494 if (len > static_cast<uint32_t>(available_space)) { | 494 if (len > static_cast<uint32_t>(available_space)) { |
495 ASSERT(!bCtrl); | 495 RTC_DCHECK(!bCtrl); |
496 len = static_cast<uint32_t>(available_space); | 496 len = static_cast<uint32_t>(available_space); |
497 } | 497 } |
498 | 498 |
499 // We can concatenate data if the last segment is the same type | 499 // We can concatenate data if the last segment is the same type |
500 // (control v. regular data), and has not been transmitted yet | 500 // (control v. regular data), and has not been transmitted yet |
501 if (!m_slist.empty() && (m_slist.back().bCtrl == bCtrl) && | 501 if (!m_slist.empty() && (m_slist.back().bCtrl == bCtrl) && |
502 (m_slist.back().xmit == 0)) { | 502 (m_slist.back().xmit == 0)) { |
503 m_slist.back().len += len; | 503 m_slist.back().len += len; |
504 } else { | 504 } else { |
505 size_t snd_buffered = 0; | 505 size_t snd_buffered = 0; |
506 m_sbuf.GetBuffered(&snd_buffered); | 506 m_sbuf.GetBuffered(&snd_buffered); |
507 SSegment sseg(static_cast<uint32_t>(m_snd_una + snd_buffered), len, bCtrl); | 507 SSegment sseg(static_cast<uint32_t>(m_snd_una + snd_buffered), len, bCtrl); |
508 m_slist.push_back(sseg); | 508 m_slist.push_back(sseg); |
509 } | 509 } |
510 | 510 |
511 size_t written = 0; | 511 size_t written = 0; |
512 m_sbuf.Write(data, len, &written, NULL); | 512 m_sbuf.Write(data, len, &written, NULL); |
513 return static_cast<uint32_t>(written); | 513 return static_cast<uint32_t>(written); |
514 } | 514 } |
515 | 515 |
516 IPseudoTcpNotify::WriteResult PseudoTcp::packet(uint32_t seq, | 516 IPseudoTcpNotify::WriteResult PseudoTcp::packet(uint32_t seq, |
517 uint8_t flags, | 517 uint8_t flags, |
518 uint32_t offset, | 518 uint32_t offset, |
519 uint32_t len) { | 519 uint32_t len) { |
520 ASSERT(HEADER_SIZE + len <= MAX_PACKET); | 520 RTC_DCHECK(HEADER_SIZE + len <= MAX_PACKET); |
521 | 521 |
522 uint32_t now = Now(); | 522 uint32_t now = Now(); |
523 | 523 |
524 std::unique_ptr<uint8_t[]> buffer(new uint8_t[MAX_PACKET]); | 524 std::unique_ptr<uint8_t[]> buffer(new uint8_t[MAX_PACKET]); |
525 long_to_bytes(m_conv, buffer.get()); | 525 long_to_bytes(m_conv, buffer.get()); |
526 long_to_bytes(seq, buffer.get() + 4); | 526 long_to_bytes(seq, buffer.get() + 4); |
527 long_to_bytes(m_rcv_nxt, buffer.get() + 8); | 527 long_to_bytes(m_rcv_nxt, buffer.get() + 8); |
528 buffer[12] = 0; | 528 buffer[12] = 0; |
529 buffer[13] = flags; | 529 buffer[13] = flags; |
530 short_to_bytes(static_cast<uint16_t>(m_rcv_wnd >> m_rwnd_scale), | 530 short_to_bytes(static_cast<uint16_t>(m_rcv_wnd >> m_rwnd_scale), |
531 buffer.get() + 14); | 531 buffer.get() + 14); |
532 | 532 |
533 // Timestamp computations | 533 // Timestamp computations |
534 long_to_bytes(now, buffer.get() + 16); | 534 long_to_bytes(now, buffer.get() + 16); |
535 long_to_bytes(m_ts_recent, buffer.get() + 20); | 535 long_to_bytes(m_ts_recent, buffer.get() + 20); |
536 m_ts_lastack = m_rcv_nxt; | 536 m_ts_lastack = m_rcv_nxt; |
537 | 537 |
538 if (len) { | 538 if (len) { |
539 size_t bytes_read = 0; | 539 size_t bytes_read = 0; |
540 rtc::StreamResult result = m_sbuf.ReadOffset( | 540 rtc::StreamResult result = m_sbuf.ReadOffset( |
541 buffer.get() + HEADER_SIZE, len, offset, &bytes_read); | 541 buffer.get() + HEADER_SIZE, len, offset, &bytes_read); |
542 RTC_UNUSED(result); | 542 RTC_UNUSED(result); |
543 ASSERT(result == rtc::SR_SUCCESS); | 543 RTC_DCHECK(result == rtc::SR_SUCCESS); |
544 ASSERT(static_cast<uint32_t>(bytes_read) == len); | 544 RTC_DCHECK(static_cast<uint32_t>(bytes_read) == len); |
545 } | 545 } |
546 | 546 |
547 #if _DEBUGMSG >= _DBG_VERBOSE | 547 #if _DEBUGMSG >= _DBG_VERBOSE |
548 LOG(LS_INFO) << "<-- <CONV=" << m_conv | 548 LOG(LS_INFO) << "<-- <CONV=" << m_conv |
549 << "><FLG=" << static_cast<unsigned>(flags) | 549 << "><FLG=" << static_cast<unsigned>(flags) |
550 << "><SEQ=" << seq << ":" << seq + len | 550 << "><SEQ=" << seq << ":" << seq + len |
551 << "><ACK=" << m_rcv_nxt | 551 << "><ACK=" << m_rcv_nxt |
552 << "><WND=" << m_rcv_wnd | 552 << "><WND=" << m_rcv_wnd |
553 << "><TS=" << (now % 10000) | 553 << "><TS=" << (now % 10000) |
554 << "><TSR=" << (m_ts_recent % 10000) | 554 << "><TSR=" << (m_ts_recent % 10000) |
(...skipping 188 matching lines...) Loading... |
743 m_snd_wnd = static_cast<uint32_t>(seg.wnd) << m_swnd_scale; | 743 m_snd_wnd = static_cast<uint32_t>(seg.wnd) << m_swnd_scale; |
744 | 744 |
745 uint32_t nAcked = seg.ack - m_snd_una; | 745 uint32_t nAcked = seg.ack - m_snd_una; |
746 m_snd_una = seg.ack; | 746 m_snd_una = seg.ack; |
747 | 747 |
748 m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now; | 748 m_rto_base = (m_snd_una == m_snd_nxt) ? 0 : now; |
749 | 749 |
750 m_sbuf.ConsumeReadData(nAcked); | 750 m_sbuf.ConsumeReadData(nAcked); |
751 | 751 |
752 for (uint32_t nFree = nAcked; nFree > 0;) { | 752 for (uint32_t nFree = nAcked; nFree > 0;) { |
753 ASSERT(!m_slist.empty()); | 753 RTC_DCHECK(!m_slist.empty()); |
754 if (nFree < m_slist.front().len) { | 754 if (nFree < m_slist.front().len) { |
755 m_slist.front().len -= nFree; | 755 m_slist.front().len -= nFree; |
756 nFree = 0; | 756 nFree = 0; |
757 } else { | 757 } else { |
758 if (m_slist.front().len > m_largest) { | 758 if (m_slist.front().len > m_largest) { |
759 m_largest = m_slist.front().len; | 759 m_largest = m_slist.front().len; |
760 } | 760 } |
761 nFree -= m_slist.front().len; | 761 nFree -= m_slist.front().len; |
762 m_slist.pop_front(); | 762 m_slist.pop_front(); |
763 } | 763 } |
(...skipping 141 matching lines...) Loading... |
905 if (seg.len > 0) { | 905 if (seg.len > 0) { |
906 if (bIgnoreData) { | 906 if (bIgnoreData) { |
907 if (seg.seq == m_rcv_nxt) { | 907 if (seg.seq == m_rcv_nxt) { |
908 m_rcv_nxt += seg.len; | 908 m_rcv_nxt += seg.len; |
909 } | 909 } |
910 } else { | 910 } else { |
911 uint32_t nOffset = seg.seq - m_rcv_nxt; | 911 uint32_t nOffset = seg.seq - m_rcv_nxt; |
912 | 912 |
913 rtc::StreamResult result = m_rbuf.WriteOffset(seg.data, seg.len, | 913 rtc::StreamResult result = m_rbuf.WriteOffset(seg.data, seg.len, |
914 nOffset, NULL); | 914 nOffset, NULL); |
915 ASSERT(result == rtc::SR_SUCCESS); | 915 RTC_DCHECK(result == rtc::SR_SUCCESS); |
916 RTC_UNUSED(result); | 916 RTC_UNUSED(result); |
917 | 917 |
918 if (seg.seq == m_rcv_nxt) { | 918 if (seg.seq == m_rcv_nxt) { |
919 m_rbuf.ConsumeWriteBuffer(seg.len); | 919 m_rbuf.ConsumeWriteBuffer(seg.len); |
920 m_rcv_nxt += seg.len; | 920 m_rcv_nxt += seg.len; |
921 m_rcv_wnd -= seg.len; | 921 m_rcv_wnd -= seg.len; |
922 bNewData = true; | 922 bNewData = true; |
923 | 923 |
924 RList::iterator it = m_rlist.begin(); | 924 RList::iterator it = m_rlist.begin(); |
925 while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) { | 925 while ((it != m_rlist.end()) && (it->seq <= m_rcv_nxt)) { |
(...skipping 56 matching lines...) Loading... |
982 nTransmit); | 982 nTransmit); |
983 | 983 |
984 if (wres == IPseudoTcpNotify::WR_SUCCESS) | 984 if (wres == IPseudoTcpNotify::WR_SUCCESS) |
985 break; | 985 break; |
986 | 986 |
987 if (wres == IPseudoTcpNotify::WR_FAIL) { | 987 if (wres == IPseudoTcpNotify::WR_FAIL) { |
988 LOG_F(LS_VERBOSE) << "packet failed"; | 988 LOG_F(LS_VERBOSE) << "packet failed"; |
989 return false; | 989 return false; |
990 } | 990 } |
991 | 991 |
992 ASSERT(wres == IPseudoTcpNotify::WR_TOO_LARGE); | 992 RTC_DCHECK(wres == IPseudoTcpNotify::WR_TOO_LARGE); |
993 | 993 |
994 while (true) { | 994 while (true) { |
995 if (PACKET_MAXIMUMS[m_msslevel + 1] == 0) { | 995 if (PACKET_MAXIMUMS[m_msslevel + 1] == 0) { |
996 LOG_F(LS_VERBOSE) << "MTU too small"; | 996 LOG_F(LS_VERBOSE) << "MTU too small"; |
997 return false; | 997 return false; |
998 } | 998 } |
999 // !?! We need to break up all outstanding and pending packets and then re
transmit!?! | 999 // !?! We need to break up all outstanding and pending packets and then re
transmit!?! |
1000 | 1000 |
1001 m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD; | 1001 m_mss = PACKET_MAXIMUMS[++m_msslevel] - PACKET_OVERHEAD; |
1002 m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula | 1002 m_cwnd = 2 * m_mss; // I added this... haven't researched actual formula |
(...skipping 100 matching lines...) Loading... |
1103 // data ready to send then hold off until we get more to send, or the | 1103 // data ready to send then hold off until we get more to send, or the |
1104 // in-flight data is acknowledged. | 1104 // in-flight data is acknowledged. |
1105 if (m_use_nagling && (m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) { | 1105 if (m_use_nagling && (m_snd_nxt > m_snd_una) && (nAvailable < m_mss)) { |
1106 return; | 1106 return; |
1107 } | 1107 } |
1108 | 1108 |
1109 // Find the next segment to transmit | 1109 // Find the next segment to transmit |
1110 SList::iterator it = m_slist.begin(); | 1110 SList::iterator it = m_slist.begin(); |
1111 while (it->xmit > 0) { | 1111 while (it->xmit > 0) { |
1112 ++it; | 1112 ++it; |
1113 ASSERT(it != m_slist.end()); | 1113 RTC_DCHECK(it != m_slist.end()); |
1114 } | 1114 } |
1115 SList::iterator seg = it; | 1115 SList::iterator seg = it; |
1116 | 1116 |
1117 // If the segment is too large, break it into two | 1117 // If the segment is too large, break it into two |
1118 if (seg->len > nAvailable) { | 1118 if (seg->len > nAvailable) { |
1119 SSegment subseg(seg->seq + nAvailable, seg->len - nAvailable, seg->bCtrl); | 1119 SSegment subseg(seg->seq + nAvailable, seg->len - nAvailable, seg->bCtrl); |
1120 seg->len = nAvailable; | 1120 seg->len = nAvailable; |
1121 m_slist.insert(++it, subseg); | 1121 m_slist.insert(++it, subseg); |
1122 } | 1122 } |
1123 | 1123 |
(...skipping 72 matching lines...) Loading... |
1196 | 1196 |
1197 if (kind == TCP_OPT_EOL) { | 1197 if (kind == TCP_OPT_EOL) { |
1198 // End of option list. | 1198 // End of option list. |
1199 break; | 1199 break; |
1200 } else if (kind == TCP_OPT_NOOP) { | 1200 } else if (kind == TCP_OPT_NOOP) { |
1201 // No op. | 1201 // No op. |
1202 continue; | 1202 continue; |
1203 } | 1203 } |
1204 | 1204 |
1205 // Length of this option. | 1205 // Length of this option. |
1206 ASSERT(len != 0); | 1206 RTC_DCHECK(len != 0); |
1207 RTC_UNUSED(len); | 1207 RTC_UNUSED(len); |
1208 uint8_t opt_len = 0; | 1208 uint8_t opt_len = 0; |
1209 buf.ReadUInt8(&opt_len); | 1209 buf.ReadUInt8(&opt_len); |
1210 | 1210 |
1211 // Content of this option. | 1211 // Content of this option. |
1212 if (opt_len <= buf.Length()) { | 1212 if (opt_len <= buf.Length()) { |
1213 applyOption(kind, buf.Data(), opt_len); | 1213 applyOption(kind, buf.Data(), opt_len); |
1214 buf.Consume(opt_len); | 1214 buf.Consume(opt_len); |
1215 } else { | 1215 } else { |
1216 LOG(LS_ERROR) << "Invalid option length received."; | 1216 LOG(LS_ERROR) << "Invalid option length received."; |
(...skipping 49 matching lines...) Loading... |
1266 } | 1266 } |
1267 | 1267 |
1268 // Determine the proper size of the buffer. | 1268 // Determine the proper size of the buffer. |
1269 new_size <<= scale_factor; | 1269 new_size <<= scale_factor; |
1270 bool result = m_rbuf.SetCapacity(new_size); | 1270 bool result = m_rbuf.SetCapacity(new_size); |
1271 | 1271 |
1272 // Make sure the new buffer is large enough to contain data in the old | 1272 // Make sure the new buffer is large enough to contain data in the old |
1273 // buffer. This should always be true because this method is called either | 1273 // buffer. This should always be true because this method is called either |
1274 // before connection is established or when peers are exchanging connect | 1274 // before connection is established or when peers are exchanging connect |
1275 // messages. | 1275 // messages. |
1276 ASSERT(result); | 1276 RTC_DCHECK(result); |
1277 RTC_UNUSED(result); | 1277 RTC_UNUSED(result); |
1278 m_rbuf_len = new_size; | 1278 m_rbuf_len = new_size; |
1279 m_rwnd_scale = scale_factor; | 1279 m_rwnd_scale = scale_factor; |
1280 m_ssthresh = new_size; | 1280 m_ssthresh = new_size; |
1281 | 1281 |
1282 size_t available_space = 0; | 1282 size_t available_space = 0; |
1283 m_rbuf.GetWriteRemaining(&available_space); | 1283 m_rbuf.GetWriteRemaining(&available_space); |
1284 m_rcv_wnd = static_cast<uint32_t>(available_space); | 1284 m_rcv_wnd = static_cast<uint32_t>(available_space); |
1285 } | 1285 } |
1286 | 1286 |
1287 } // namespace cricket | 1287 } // namespace cricket |
OLD | NEW |