diff --git a/.arclint b/.arclint index 4b320b6e44..e3a69c5ca6 100644 --- a/.arclint +++ b/.arclint @@ -10,6 +10,7 @@ "debug-mode" : false, "options" : [ "-nodefinc", + "-std=c++17", "-cl='set ident_header bsls_ident'" ], "header-paths" : [ diff --git a/.bde-format b/.bde-format new file mode 100644 index 0000000000..cd07f981fd --- /dev/null +++ b/.bde-format @@ -0,0 +1 @@ +BasedOnStyle: BDE diff --git a/debian/changelog b/debian/changelog index 49abaa73ce..78744db286 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,16 +1,359 @@ -bsl-internal (3.82.0.0) unstable; urgency=low - +bsl-internal (3.98.0.0) unstable; urgency=low + + * 66080e631 Bumped version to 3.98.0.0 + * 681afa3ef bslstl_stringview: Make 'substr' conform to the standard (#3536) + * 954ddeb47 Balst disable resolving drqs 167270727 (#3507) + * 4ffdf8a06 Update bdlmt_threadmultiplexor.h (#3535) + * 9a9c3e146 Bslh wyhash final v3 2 drqs 150989750 (#3530) + + -- Mike Giroux Fri, 14 Jan 2022 13:39:29 -0500 + +bsl-internal (3.97.0.0) git; urgency=low + + * 59cbc48d7 Changelog for version 3.97.0.0 + * dc2d49a39 Bumped version to 3.97.0.0 + * 038ecc280 Fix missing shared object initialization (#3526) + * 0055c554f Merge pull request #3512 from bchapman/balst-DWARF-5-drqs-167927647 + * 2680c69df Data race has been fixed in 'bslstl_hashtable' (#3518) + * 60079c565 DO NOT MERGE: correct 'waitUntilEmpty' race with 'disablePopFront' (#3521) + * 2e8e6b2e0 Parenthesize missed bsl::min to work around Windows issue (#3524) + + -- Mike Giroux Sun, 9 Jan 2022 08:59:41 -0500 + +bsl-internal (3.96.0.1) git; urgency=low + + * c1e082310 Changelog for version 3.96.0.1 + * cd297fc25 Revert PerformanceMonitor API method (#3523) + + -- Mike Giroux Mon, 20 Dec 2021 18:14:34 -0500 + +bsl-internal (3.96.0.0) git; urgency=low + + * b97be140b Changelog for version 3.96.0.0 + * 028ac5b32 Bumped version to 3.96.0.0 + * ce02a9d6a Merge pull request #3461 from bchapman/bslmf_isconvertible-enhance-doc-drqs-167585149 + * 13b36d061 Fix LocalSequentialAllocator stack alignment on 32-bit windows builds. (#3510) + * 26a957eb7 Revert "baljsn: Support arrays of arrays (#3457)" (#3517) + * 8a406cc0e Fix misplaced endif; thanks to MIKE WELLER for the help debugging (#3515) + * 64546a875 'at' methods have been added to 'bdlc::FlatHashMap' (#3511) + * ac80b7ca7 Remove pcre2 config from dpkg (#3513) + * 72a91fa69 'sregex_iterator' support has been added (#3506) + * 8510bb9d9 baljsn: Support arrays of arrays (#3457) + * 148197ca3 Fix AtomicInt cast to 64-bit ints on optimized Solaris. (#3494) + * 1e267952e Add class template deduction guides to bsl::priority_queue (#3496) + * 54983f1a1 Merge pull request #3498 from mclow3/CTAD-map + * cc6c2af2b Issue with const rvalue assignment has been fixed in 'bdlb_variant' DRQS 167585741 (#3481) + * 7b1e68c87 balb: BDE-4.0 interfaces (#3426) + * ea3af9bd5 Remove unused typedef (#3492) + * 4fe678e7c update 'swap' function-level doc for 'bsl' containers (#3504) + + -- Mike Giroux Fri, 17 Dec 2021 10:20:40 -0500 + +bsl-internal (3.95.0.0) git; urgency=low + + * 68803d5de Changelog for version 3.95.0.0 + * f84e912f4 Bumped version to 3.95.0.0 + * f34c01162 Drqs 167916160 baljsn balxml ryu changes (#3503) + * 15a7b2b76 Merge pull request #3490 from mclow3/CTAD-StdConvertible + * 2e3f2eeb7 Transparent comparators have been added. DRQS 164220683 (#3462) + * b667a44df Merge pull request #3501 from jmendelsohn4/drqs-167903305/bslmt_timedsemaphore-nb-20211201 + * 653b33954 Merge pull request #3499 from sbreitst/bdlt_datetime-doc-drqs-167892298 + * 6c97a6aa0 cleaning up ; warnings in uses of BSLS_LINKCOERCION (#3497) + * e2cd038ac Fix accidentally-quadratic repeated calls to BlobUtil::append() (#3483) + * e6d7ef0e8 Merge pull request #3495 from jmendelsohn4/drqs-166024031/bslmt_timedsemaphore-windows-early-return + * 971464b11 Add class template deduction guides to bsl::stack (#3487) + * e01223f0e Add class template deduction guides to bsl::queue (#3488) + * 792a23cd1 Refactor s_baltst: Provide 1-1 map from XSD's to components (#3470) + * 5d10a65e5 Add class template deduction guides to bsl::deque (#3480) + * 69fe2bfe3 Add class template deduction guides to bsl::vector (#3479) + * ff445582b Add template deduction guides for bsl::list (second try) (#3468) + + -- Mike Giroux Fri, 3 Dec 2021 20:06:37 -0500 + +bsl-internal (3.94.0.0) git; urgency=low + + * f31e8f001 Changelog for version 3.94.0.0 + * 36dc0b484 Bumped version to 3.94.0.0 + * 53e349a58 Baljsn datumutil decode key error drqs166186464 (#3469) + * 668dfca7f Pool memory for shared ptr ball record (DRQS 165886076) (#3465) + * f838a3208 drqs-166843299: 'balcl_commandline': Initial: fix runtime error. (#3482) + * a34f848eb Fixup minor typos in grammar rules in bdlb_numericparseutil (#3477) + * 0821c1819 Merge pull request #3467 from afeher/drqs-167627126-buffer-size-constants-into-bslalg_numericformatterutil + * 8283362c7 bdlcc_skiplist: fix to race condtions DRQS's 167644288 / 167716470 (#3472) + * 367cb045a Fix NumFmtUtil test driver for gcc cpp03. (#3471) + * d3a8ca616 *DO NOT MERGE YET* Version 2: add C++11 move constructor/assignment to managed ptr 166101527 (#3358) + * c8b2415c6 drqs-167627529: 'bslh_hashpair.t.cpp': Nightly build issues. (#3466) + * c1d8e6d89 adding tests for clocks matching (#3464) + + -- Mike Giroux Sat, 20 Nov 2021 22:29:36 -0500 + +bsl-internal (3.93.0.0) git; urgency=low + + * e4f76793c Changelog for version 3.93.0.0 + * 70c805ada Bumped version to 3.93.0.0 + * d4e4b0493 bde-4.0 for 'bdlde::Utf8Util::appendUtf8CodePoint' (#3450) + * 915d3b7a3 bbldc: BDE 4.0 interfaces (#3435) + * da87d02db drqs-167477789: 'bslh_hashpair': Initial. (#3452) + * 72176ef6f add security warning to bslx package-level doc (DRQS 167106830) (#3454) + * b1c60d6b3 Simcpp11 fix split test driver guard macros drqs166312437 (#3455) + * 82f9cbfa8 drqs-100939440: 'bslh_hashtuple': Initial. (#3445) + * 45d8f0d93 Leaky bucket related components have been moved from bte to bal. DRQS 167074983 (#3449) + * af89d04ff Class template deduction guides for bsl::optional (#3414) + * 7a93b4e36 Merge pull request #3448 from sbreitst/bacl_typeinfo-leak-drqs-167497166 + + -- Mike Giroux Fri, 29 Oct 2021 10:55:42 -0400 + +bsl-internal (3.92.0.0) git; urgency=low + + * 897a59c74 Changelog for version 3.92.0.0 + * ee21ddea8 Bumped version to 3.92.0.0 + * 43a86c70e Class template deduction guides for shared_ptr and weak_ptr (#3415) + * 44c831040 bdlcc_skiplist: 'removeAll' zeroing out 'd_next_p' in nodes (#3447) + * bf8ccb5cf Comments with canonical headers have been added (#3442) + * 7c1b03fe8 Documentation inconsistency in 'bsl::basic_string' has been removed (#3446) + * d4f34926c Merge pull request #3434 from bchapman/bblb-make-pmr-compatible-drqs-167357938 + * af997368f Merge pull request #3423 from bchapman/bdlb_nullablevalue-noexcept-ctors-drqs-167273350 + * 3f3a4f93c Print helpers for 'bsl::optional' (#3425) + * 5e5cdcc96 Fix C++03 compilation (#3444) + * 7759236c4 Non-const indexer has been made constexpr in 'bsl::array'. DRQS 166841800 (#3418) + * da7e9b3c0 Balst stacktraceprinter create drqs 167052072 (#3392) + * 6bbe13a53 Add support for Base64url encoding to bdlde::Base64Encoder (#3417) + * 2a2ab266e 'std::chrono' range checks have become public. DRQS 167369799 (#3427) + * 8b59fbef3 bslmf::If -> bsl::conditional (#3441) + * f02bad262 Removed BSLMF_ASSERT that won't compile on C++03. (#3438) + * 443f6074f Merge pull request #3364 from bchapman/balm-make-pmr-compatible-drqs-166649777 + * eb8d6ae91 Merge pull request #3331 from bchapman/baltzo-make-pmr-compatible-drqs-166649676 + * 2a6f052f5 Merge pull request #3419 from bchapman/bdlcc-make-pmr-compatible-2-drqs-166649762 + * fe9878966 Merge pull request #3410 from bchapman/balcl-make-pmr-compatible-drqs-166649786 + * be044f278 Refactored TempDirectoryGuard (#3354) + * ffea60e7d Regenerate cpp03 file for bslstl_optional. (#3437) + * 160dc5d04 Cleanup documentation. (#3436) + * 927696159 Merge pull request #3420 from bchapman/bslmt_semaphone-benchmark-creators-drqs-166698942 + * 3c3af7b5d 'bdlbb::BlobUtil::hexDump' has been improved. DRQS 167253580 (#3422) + * f7cd095ce Merge pull request #3433 from mgiroux/unwind-FixedThreadPool-DRQS167232024 + + -- Mike Giroux Fri, 15 Oct 2021 10:08:32 -0400 + +bsl-internal (3.91.0.0) git; urgency=low + + * 052dfbd47 Changelog for version 3.91.0.0 + * b72a38de5 Bumped version to 3.91.0.0 + * 25d5eb98d Class template deduction guides for bsl::array (#3421) + * 2bed1afaa Fix bug in CTAD enable_if codes (#3424) + * 20d76018d Implement template deduction guides for `bsl::pair` (#3405) + * 5584c1dd8 ball::FileObserver2: Add a flag to disable timestamp append. (#3380) + * 51ea06097 balb_performancemonitor: tolerate spaces in name of main thread on Linux (#3367) + * c103732b1 improve performance of 'reserve(0)' on empty container (#3416) + * dfad27484 bsls_stackaddressutil: eliminate stray nulls at end (#3409) + + -- Mike Giroux Fri, 1 Oct 2021 12:49:14 -0400 + +bsl-internal (3.90.0.0) git; urgency=low + + * 9db77e8a6 Changelog for version 3.90.0.0 + * 93605b383 Bumped version to 3.90.0.0 + * bc7513919 Merge pull request #3411 from bde/INTEGRATION-drqs-167113122-shortest-round-trip-xml-json-floating-point-encoding + * 3b2ae0071 drqs-153900553: 'balcl': Support 'bsl::optional' linked variables. (#3390) + * 91f988f67 Eliminate use of back_insert_iterator in baljsn::ParserUtil (#3408) + * b229294fa Revert to FileDescriptor being void*. (#3407) + * f3c7b2644 fix intorduced compile error on MacOS (#3406) + * 8f629f76f add new type_trait 'is_allocator' for use in new code (#3375) + * be51eb95b Update bdls for BDE 4.0 (#3241) + * 08f490648 changed files from old patch (#3404) + * fb3b47a79 correcting data race on 'd_owner' and some whitespace issues (#3400) + * 58175d5ce Remove deprecated, unused method in ReaderWriterLock. (#3397) + * 6ff92ffdc Add missing allocator trait. (#3398) + + -- Mike Giroux Fri, 17 Sep 2021 17:13:41 -0400 + +bsl-internal (3.89.0.0) git; urgency=low + + * da884973c Changelog for version 3.89.0.0 + * d5f1f3c24 Bumped version to 3.89.0.0 + * 75b841a5e bdlc_flathashtable: Address {DRQS 167125039} (#3394) + * 10aff94a1 Implement the view/str functionality from P0408 (#3374) + * 81b810d9c Fix SA test case for char* (#3391) + * a5ad72449 Fix assorted typos. (#3389) + * f1e6b7964 Scoped attribute support more types drqs 166238785 (#3378) + * f2b9961c0 Compiler warnings have been suppressed in 'bdlb_indexspanstringutil' (#3388) + * 642555fe9 Fixes in 'bdlb_indexspanstringutil' for MSVC. DRQS 166689011 (#3362) + * f2ed88ce3 Merge pull request #3382 from afeher/drqs-167048561-yet-another-unreferenced-ryu-for-master + * 2fc134d6c bdlc_flathashtable: Silence GCC 7 unknown pragma warning (#3385) + * 37e926a10 fix doc for string move constructor (#3384) + * e7bb54fdc Fix typo in resolveSymLinksIfAny (#3383) + * 07cbe6595 Stop bsl_type_traits.h show error on cpp03 (#3369) + * c3f736da4 Contract of allocate shared vs allocate managed (DRQS 166899748) (#3373) + + -- Mike Giroux Fri, 3 Sep 2021 11:06:53 -0400 + +bsl-internal (3.88.0.0) git; urgency=low + + * 016dff5d5 Changelog for version 3.88.0.0 + * 11f96f7ce Bumped version to 3.88.0.0 + * e26cd0adb fix build breakage in bdlat_arrayfunctions.t (#3379) + * 413ca706f drqs-149019607: 'bdlat': Updated Usage in several "TBD:" components. (#3287) + * 3a1d36cab bdlc_flathashtable: Separate copy paths for trivial and nontrivial types (#3366) + * f13fb6b20 Correct behavior of scoped attribute format specifiers. (#3361) + * cd05b19b7 check for null in case the Event has expired (#3370) + * c997bcabb Drqs 166936444/bdlmt fixedthreadpool nb 20210810 (#3365) + * c6c7931b0 drqs-166390563: 'balcl' component documentation fixes. (#3342) + * 7c00865f6 Drqs 165748105/bdlmt fixedthreadpool use bdlcc boundedqueue (#3314) + + -- Mike Giroux Fri, 20 Aug 2021 13:44:01 -0400 + +bsl-internal (3.87.0.0) git; urgency=low + + * c113ffda6 Changelog for version 3.87.0.0 + * ee39d04d6 Bumped version to 3.87.0.0 + * e430ecb7d Merge pull request #3363 from afeher/drqs-166905817-fix-ryu-decimal-notation + * 44c970982 bdld_datum.h: doc formatting fixes (#3231) + * a5e8942d6 make EventData, RecurringEventData allocator-aware (DRQS 166874007) (#3359) + * 2062197f8 Fix friend declaration in balxml. (#3355) + * 2e9e89e55 Merge pull request #3279 from mgill83/asyncfileobserver-test-fix-drqs-166463531 + * 93a379424 balst_stacktraceframe: bde-4.0 (#3348) + + -- Oleg Subbotin Fri, 6 Aug 2021 18:31:56 -0400 + +bsl-internal (3.86.0.0) git; urgency=low + + * 293bc9b28 Changelog for version 3.86.0.0 + * e8e7c2bcf Bumped version to 3.86.0.0 + * 34c3a0cf6 Use an alternative approach to creating a zero-terminated string. (#3347) + * 34d40e6f4 Fix minor package documentation typos (#3352) + * 93628974e Read-only 'visit' has been added to 'bdlcc_stripedunordered*' containers (#3340) + * ea9a08e4c balxml_formatter: Indent closing tags when 'wrapColumn' is 0 (#3312) + * 85c4332a1 balber: optionally support preserveNegativeZero (#3341) + * bbb277fff bslstl_function: Test ptr-to-data-member support & fix MSVC 2015 build (#3295) + * 228f35172 Documentation inconsistency between prefixes for exceptions has been removed (#3346) + * b46dfecd9 Chrono changes for bdlmt::EventScheduler (#3192) + * f53494ada Revert "Revert "'bdldfp' package has been updated in accordance with bde 4.0 (#3300)" (#3326)" (#3345) + * 5e057e052 Add Ryu with Bloomberg extensions. (#3343) + * e55aab5f5 Revert "Revert "Revert "'bdldfp' package has been updated in accordance with bde 4.0 (#3257)" (#3288)" (#3334)" (#3344) + * c37a4b25e bdlmt::EventScheduler::isInDispatcherThread (#3307) + * f89e13d90 Remove signaling NaN support in 32bit Datum. (#3338) + * f3264a6cb Issue with non-copyable return values in 'bdlb::Variant' has been fixed (#3255) + * e5f573896 Required include has been added to the 'bsl_shared_mutex.h' (#3339) + * 801ba7ba9 balber_berutil: minor test driver improvements and warning cleanup (#3281) + * 375789aeb Merge pull request #3306 from bchapman/bdls_filesystemutil-truncateFileSize-drqs-164967919 + * 695d027b0 Revert "Revert "'bdldfp' package has been updated in accordance with bde 4.0 (#3257)" (#3288)" (#3334) + * 2a4fee990 Fix signaling NaN testing. (#3323) + * 62953a061 Add pcre2 patch info to README.Bloomberg.md (#3332) + * 9ae02e6a2 Merge pull request #3325 from glaptev1/add_blob_replace_data_buffer_drqs-153713331 + * bd4882e62 Fix sljit platform detection with xlc on AIX (#3329) + * c74e592d0 Revert "Add C++11 move constructor/assignment to managed ptr 166101527 (#3240)" (#3328) + * 5cec5d4bb Repect the field width when output the bsls::TimeInterval value (#3322) + * b04528869 Fixed bdlde build error on Windows(C++17) (#3327) + + -- Oleg Subbotin Fri, 30 Jul 2021 17:30:58 -0400 + +bsl-internal (3.85.0.0) git; urgency=low + + * 136e4356b Changelog for version 3.85.0.0 + * 6400527d3 Bumped version to 3.85.0.0 + * 66a11f2a0 Revert "'bdldfp' package has been updated in accordance with bde 4.0 (#3300)" (#3326) + * 6ed246583 Add C++11 move constructor/assignment to managed ptr 166101527 (#3240) + * ec5e7ff16 'HasStlIterators' trait has been added for 'bsl::array' (#3316) + * a5aa43c26 bdlcc::ObjectPool::{release,delete}Object: doc undefined if already released (#3324) + * 55a9c062b bdlt - bde 4.0 (#3305) + * 9dbf0a7b3 bde 4.0 bdlde drqs 165468367 (#3262) + * 3eb68fe49 balxml: bde-4.0 (#3261) + * a8a380413 'bdldfp' package has been updated in accordance with bde 4.0 (#3300) + * 7cad48d37 bslstl_iterator: add 'size', 'ssize' free functions (#3250) + * 3e90d4527 balxml_configschema: Fix test failures (#3317) + * 30d2c39f4 'shared_mutex' has been added to 'bsl' namespace (#3302) + * 3d7afa56b Revert "Revert "Balxml decoder check utf 8 with bug 2 drqs 158940938 … (#3315) + * d9054e7a8 balxml_configschema: Test XSD for validity and correctness (#3292) + * 9ab3b91c6 bdlcc_skiplist: Fix Clang C++03 Build (#3304) + * acc887352 Disable usage example for AIX in bslmf_forwardingtype.t.cpp (#3313) + * e452b3bb2 Fuzz tests have been added for 'bdlde_utf8util' (#3246) + * 0bfa8c234 Drqs 166535994/bdlcc boundedqueue wait until empty (#3299) + * f5b053885 add 'arrive' (#3301) + * 748766706 Links have been updated in ball.txt file. (#3303) + * 4d2d8d311 Add chrono-style overloads to bdlmt::throttle (#3283) + + -- Mike Giroux Fri, 9 Jul 2021 13:46:13 -0400 + +bsl-internal (3.84.0.0) git; urgency=low + + * a801c3a54 Changelog for version 3.84.0.0 + * 3c399e7fd Bumped version to 3.84.0.0 + * 57598d5b2 Set BSLS_LIBRARYFEATURES_HAS_CPP17_DEPRECATED_REMOVED in clang if _GLIBCXX_USE_DEPRECATED is defined (#3296) + * 30c73d95a Add bplpcre::RegEx::replace method (#3259) + * e1b5f9a96 Make function assignment from reference wrapper noexcept (#3260) + * a00e334e0 Fix case typo in doc (#3294) + * f77ccddfd Revert "'bdldfp' package has been updated in accordance with bde 4.0 (#3257)" (#3288) + * 86bcada92 Remove obsolete workaround. (#3284) + * 95153ce67 Backfill changelog (bumping patch version) + * 820c397a0 Update pcre2 to v10.37 (#3270) + * f117339b2 'bdldfp' package has been updated in accordance with bde 4.0 (#3257) + * 2e2060690 Regenerate cpp03 files. (#3278) + * 5122c1c13 baljsn_parserutil: stop assuming that string_view::const_iterator is 'const char *' (#3275) + * 7f577a9e9 Changes to 'bsl::shared_ptr' assignment operator documentation (#3263) + * 04de15c52 Remove bsl_new non-standard exports for gcc and clang (#3266) + * 4088cca3d Silence unused variable warnings in ball. (#3276) + + -- Mike Giroux Fri, 25 Jun 2021 12:17:24 -0400 + +bsl-internal (3.83.0.0) git; urgency=low + + * bd00631a1 Changelog for version 3.83.0.0 + * 136eca751 Bumped version to 3.83.0.0 + * cfa9061e5 bsls_compilerfeatures -- doc inclusions needed for 'forward' (DRQS 166358636) (#3271) + * 9ec6d8712 Typos in documentation for bslmf_invokeresult.h (#3272) + * 245dbf31f Fix null pointer dereference UBSAN warning (#3206) + * 284f549f0 baljsn: bde-4.0 (#3154) + * ebaaad67a Fix bind forwarding drqs 165560983 (#3197) + * 9da1c6e5e Fix formatting typos. (#3268) + * a10692450 Split invokeresult.t into 8 tests (#3252) + * 6bc77d5f4 Apply foilOptimizer() call to failing tests (#3254) + * a707c014c bslstl_stringref: add c'tor from pmr::string (#3265) + * 0634bdc63 Bdlb numericparserutil string view drqs 164339936 (#3256) + * 6a7caf172 Bdlpcre bde 4.0 drgs 165911797 (#3234) + * 5775bafb6 Windows issues in 'bdlb::tokenizer' have been fixed (#3251) + + -- Mike Giroux Sun, 13 Jun 2021 14:12:14 -0400 + +bsl-internal (3.82.0.0) git; urgency=low + + * bac433c7c Changelog for version 3.82.0.0 * fdab06e66 Bumped version to 3.82.0.0 + * 4235803b7 bslstl_function: Implement Overload Limiting (#3166) + * 5753d8e19 Bde4 guidutil drqs164338852 (#3245) + * 51352144c bslstl_iterator: back out addition of 'size', 'ssize' free frunctions (#3248) + * 68c126bd8 'bdlb_tokenizer' has been updated in accordance with bde-4.0 (#3242) + * c0a749483 Make bsl::string_view trivially copyable (#3205) + * a4ba0f94c bdlmt_signaler.t.cpp: workaround for compiler bug DRQS 166134166. Wi… (#3237) + * ffca49496 Revert "Balxml decoder check utf 8 with bug 2 drqs 158940938 (#3165)" (#3225) * d3a0033b5 document implicit conversion to bool w/C++03 (DRQS 166024189) (#3223) + * 863f06ef5 bslstl_iterator: std::size natively defined for MSVC 2015 and above (#3232) + * 05fa19a17 bdld.txt: fix typos (#3230) + * a2a09a93c higher BSLS_PLATFORM_CMP_VERSION number to address heap issue (#3227) * bc2628476 Merge pull request #3021 from bchapman/bslstl_iterator-size-ssize-drqs-158473072 + * 36bc31434 bdlb_caselessstringview* (#3217) + * fd647401e 'int' overflow in 'bdlb_bitmaskutil' has been fixed (#3214) + * d0024821a Xlc16 stackaddressutil.t fix 165653925 (#3220) + * 8ab828266 remove unneeded bslmt_threadutil to remove cycle (#3221) - -- Oleg Subbotin Fri, 28 May 2021 16:42:51 -0400 + -- Oleg Subbotin Fri, 28 May 2021 16:44:34 -0400 bsl-internal (3.81.0.0) git; urgency=low * 9375df531 Changelog for version 3.81.0.0 * 9546277e5 Bumped version to 3.81.0.0 + * a2173f85c Explicitly scope seconds to avoid ADL issues with legacy code (#3219) + * 3d15703d7 Baljsn encoder assert fix drqs165920075 (#3216) + * d4ac43180 Balxml decoder check utf 8 with bug 2 drqs 158940938 (#3165) + * d7bc33a96 Md5: modified copy constructor to address DRQS 165984295 (#3209) + * cd122b3f3 Add message to d_logStream if input stream is not good (#3215) + * 54a95f95e bslalg_arraydestructionprimitives: eliminate UB in safe build (#3211) + * 4ffb53067 Correct dup2 calls for AIX and MS test drivers (#3212) + * f9fe0f100 remove 'noexcept' from test drivers (#3210) + * 31e73ec0c Remove abominable function pointer test on XLC 16.01 pending compiler fix (#3185) + * b60c811ae Add chrono-based sleep/sleepUntil method to ThreadUtil (#3201) + * 136469b49 Add workaround for __has_include issue in cppcheck (#3200) + * d64466838 Fix construction of nested bsl_optional objects (#3198) * 63bdaafdc Change 'SaturatedTimeConversionImpUtil::toMillisec' to round up fractional msec (#3203) + * 8865a0b34 Switch to ftruncate for bdls::FileSystemUtil::growFile (without fsync) - DRQS 165768099 (#3204) -- Mike Giroux Fri, 7 May 2021 10:55:59 -0400 @@ -18,6 +361,11 @@ bsl-internal (3.80.0.0) git; urgency=low * fa2fbab95 Changelog for version 3.80.0.0 * 3a6432a03 Bumped version to 3.80.0.0 + * 3f368a1ed Bslstl string view compare util drqs 164229251 (#3190) + * 9c174f124 Allow log rotation start time to be in UTC or local time. (#3178) + * aa50d3778 bslmf_movableref: Work around XLC 16 defect (#3202) + * b21b3177b Change ibm2->ibmTwo and linxdev5->linxdevFive to avoir deprecation wa… (#3199) + * f36727c07 Make bsljsn::DatumUtil::decode exception-neutral (#3196) -- Oleg Subbotin Fri, 23 Apr 2021 15:48:16 -0400 @@ -25,6 +373,14 @@ bsl-internal (3.79.0.0) git; urgency=low * e0c887c01 Changelog for version 3.79.0.0 * 1a72ef52e Bumped version to 3.79.0.0 + * af78c6118 Change to using enable_if on the function return type to deal with GCC9 issue (#3195) + * 58d071933 Flush output stream after formatting a log record (ball::RecordJsonFormatter) (#3191) + * 4f1dfb6e5 Revert "Add chrono-based `sleep`/`sleepUntil` methods to ThreadUtil (#3182)" (#3193) + * 3f29f59ad 'getSystemTemporaryDirectory' function has been added (#3189) + * 56c382378 Remove windows.h from bsls_outputredirector.h (#3138) + * 684fcde8d Add ball RecordJsonFormatter (#3086) + * 333d948f2 remove include causing issue (#3188) + * efceb87e8 Add chrono-based `sleep`/`sleepUntil` methods to ThreadUtil (#3182) -- Mike Giroux Fri, 16 Apr 2021 17:43:55 -0400 @@ -34,6 +390,12 @@ bsl-internal (3.78.0.0) git; urgency=low * b0bfce3ed Changelog for version 3.78.0.0 * eff2db0da Bumped version to 3.78.0.0 * 8a48c7fc9 Merge pull request #3183 from afeher/drqs-165758572-bdld_datummaker-docu-fix + * 2f690d1b8 Fix typo: table -> able (#3180) + * 45953ece9 add 'capacity' (#3179) + * f948085b0 Add `chrono` calls to Condition and Sluice (#3176) + * 579b3d7bd Add `chrono` calls to Barrier, Latch, and FastPostSemaphore (#3173) + * 5212c80af string::append with 'string_view' parameter. DRQS 162627462 (#3163) + * e67ace9c6 Drqs 165223581/bdlc flathash add hint insert (#3175) -- Mike Giroux Fri, 9 Apr 2021 12:07:51 -0400 @@ -41,12 +403,23 @@ bsl-internal (3.77.0.0) git; urgency=low * 5135e7ab8 Changelog for version 3.77.0.0 * efa74c608 Bumped version to 3.77.0.0 + * 9da844785 remove abominable tests from xlc 16.01 pending IBM compiler fix (#3174) * 38a526596 Merge pull request #3159 from afeher/drqs-164339013-indexspanstringutil-BDE-4.0 + * cc9a80596 reproduce and correct issue (#3171) + * 598e458a9 Fix bdlb::String::areEqualCaseless contract. (#3172) + * 064096ccb Add 'e_TIMED_OUT' and 'clockType()' to bslmt primitives (#3134) + * f64098a0f Remove use of 255 as special value in ChronoUtil_ToBslsSystemClockType; make it return success & value (#3170) + * 33b67f68f Add package bslmt::ChronoUtil; rework TimedSemaphore to use new package (#3126) * 505491bef Merge pull request #3162 from bchapman/bdlmt-bslmt-use-bslmt_testutil-drqs-165533928 + * 95eae25b0 Fix asyncfileobserver crash (DRQS 165535115 (#3164) + * e44713a27 Fix bdlbd_nullablevalue.t on xlC16. (#3158) * 87a743760 Merge pull request #3156 from afeher/drqs-127796905-add-bdld_datum-hashappend + * e9949d02c fix compiler warnings (#3160) + * ba9678997 A couple of issues have been fixed in 'bdls_filesystemutil' test driver (#3157) * 238251952 Merge pull request #3151 from afeher/drqs-165417973-bdld_datum-32bit-preserve-snan-qnan * 067231560 Merge pull request #3152 from afeher/drqs-165268497-balc_optionvalue-test-xlC16-fix * e55c86081 Merge pull request #3148 from afeher/drqs-165268497-fix-balxml-en-decoder-tests-aix + * 77be92c89 Fix DRQS 164241820: Empty compatible 'bsl::function' double-wraps. (#3143) -- Mike Giroux Sun, 28 Mar 2021 10:11:29 -0400 @@ -54,11 +427,27 @@ bsl-internal (3.76.0.0) git; urgency=low * 1c9e443c7 Changelog for version 3.76.0.0 * 7008cc9eb Bumped version to 3.76.0.0 + * 95db27194 Revert "'bsl::string::append' with 'bsl::string_view' parameter. DRQS 162627462 (#3103)" (#3150) + * 97ad2ffae Remove unused files. (#3149) + * 672cb528f Revert "Revert "Change AsyncFileObserver to use BoundedQueue (#3079)"" (#3135) + * 038f0b10c 'bsl::string::append' with 'bsl::string_view' parameter. DRQS 162627462 (#3103) + * 0b81d6456 bsl::hash' specialization for 'std::monostate' has been added (#3110) + * 93bddc980 Bde optional bsl header (#3146) * a0d83b389 Merge pull request #3145 from bde/nburgers-patch-1 * da8c9d57c Merge pull request #3144 from bchapman/bsl+bslhdrs.pub-make-consistent-drqs-165406123 + * a77e05c23 Add a warning to RecordAttributes::message (#3121) + * 81d92ab31 bslstl_function: Accept functor via forwarding ref (#3100) * 1679ccb69 Merge pull request #3139 from osubbotin/review/fix-sprintf-format-arguments + * 7dcab9a7b baljsn_encoder: pass proper bslstl::StringRef's to 'dispatcher's (#3141) + * 88b4ec32c Fix assorted typos. (#3140) + * 117b50224 gcc9 look for 2018 TBB to enable parallel algo (#3137) + * b6b4dc361 BDE 4.0 interfaces: ball (#3118) + * 74dc73ffc Remove windows.h from bsls_bsllock includes. (#3128) * 78905ff5a Merge pull request #3130 from dsokolov7/review/bde-libraryfeatures-macros-split-drqs-165206618 * 6dbaa0ffa Merge pull request #3123 from dsokolov7/review/bde-libraryfeatures-macros-renaming-drqs-165206589 + * 5315248f9 bslim_formatguard: create component (#3114) + * df066ca62 Changed safe asserts into normal asserts, also cleaned up some test … (#3113) + * eb3be1828 Fix typos in docs. (#3127) -- Mike Giroux Fri, 5 Mar 2021 12:21:51 -0500 @@ -66,7 +455,13 @@ bsl-internal (3.75.1.0) git; urgency=low * cbabb0c34 Changelog for version 3.75.1.0 * a64a1b32e Bumped version to 3.75.1.0 + * 31aa64203 Revert removal of e_END from ball_transmission.h (#3125) + * a7b2edcb1 Drqs 165258625/bdlc flathashtable ref to temprary (#3119) + * b1585cad0 Fix false assertion on null pointer. (#3116) + * 3cf58b764 Implement chrono-based calls for TimedSemaphore (second try) (#3117) * 26c475831 Merge pull request #3112 from afeher/drqs-164339060-bdlb_literalutil-bde-4.0 + * 3bb9aec4e Change AsyncFileObserver to use BoundedQueue (#3079) + * f84215ff2 Drqs 164984269/bdlcc boundedqueue remove all issue (#3082) -- Mike Giroux Sat, 20 Feb 2021 19:15:54 -0500 @@ -74,7 +469,11 @@ bsl-internal (3.75.0.0) git; urgency=low * 54e9ea68f Changelog for version 3.75.0.0 * ce7178c5c Bumped version to 3.75.0.0 + * 5a01aa530 Revert "Revert "(#3097) and (#3090)"" (#3115) * 8a1331385 Merge pull request #3111 from cwilson/bdlb_string-gcc9-compile-fail-drqs-165184815 + * fd457f652 Overload resolution for RecordStringFormatter ctors (#3105) + * 4cf8cf57c Fixed issue with 0-sized blob data buffers in 'BlobUtil::hexDump' (#3109) + * 57ffaf553 remove RecordAttributes::message() from ball (#3101) -- Mike Giroux Fri, 12 Feb 2021 16:42:15 -0500 @@ -82,7 +481,16 @@ bsl-internal (3.74.0.0) git; urgency=low * 8826c71fc Changelog for version 3.74.0.0 * 63181e22a Bumped version to 3.74.0.0 + * 2e8dd354f Revert "'bsl::hash' specialization for 'std::monostate' has been added (#3000)" (#3108) + * 1377dc9d5 Revert "Bslstl string view comparisons drqs 164229251 (#3041)" (#3106) * 807a411b3 Merge pull request #3099 from mgill83/timout-parameter-rename-drqs-164067283 + * 662311fc3 Revert "Revert "Update reference_wrapper to alias std (#2985)" (#3022)" (#3038) + * 8ce37dc37 bsltf: make tests check whether d'tos are being run (#3084) + * 7df85163d Fix typo in bdlmt_fixedthreadpool.h (#3104) + * 1a3ee1871 Minor documentation fixes. (#3102) + * 2c26e454b 'bsl::hash' specialization for 'std::monostate' has been added (#3000) + * 6bd2ea6dc bde 4.0 bdlb_string (DRQS 164339970) #2 (#3045) + * d188b2584 Bslstl string view comparisons drqs 164229251 (#3041) -- Mike Giroux Sat, 6 Feb 2021 16:45:11 -0500 @@ -98,7 +506,18 @@ bsl-internal (3.73.0.0) git; urgency=low * 075494aa4 Changelog for version 3.73.0.0 * 123eaaabf Bumped version to 3.73.0.0 * 410e4ac08 Merge pull request #3094 from bchapman/bdlb_printmethods-overload-char-drqs-164959084 + * 858bd5aa5 Revert "'bsl::string' methods 'append' and 'insert' with 'string_view' param. DRQS 162627462 (#3091)" (#3098) + * cd25f8a4b Nullable value fix for assignment (#3097) + * 80b7a97f7 'bsl::string' methods 'append' and 'insert' with 'string_view' param. DRQS 162627462 (#3091) + * 0c6ae1772 bdlde_base64[en,de]coder: Add fuzz test (#3008) + * 70b1334d3 Documentation for user-defined literals has been modified (#3093) + * 42f2827dd bsl::optional and bsl::optional backed nullablevalue (#3090) + * 9fa8567db Fix logging char(0). (#3095) + * 2fdc5a2c8 Disabled extended constexpr rules for gcc 5. (#3087) + * 7e79685f6 Support of default constructed StringRefs has been added to Tokenizer. DRQS 164650579 (#3048) * 353f25e62 Merge pull request #3083 from mgill83/bind-add-non-const-operator-support-drqs-164900532 + * 47f031f88 Remove expected -nan output from double tests (#3088) + * 2506e1943 Remove support for rendering "-nan" in baljsn_printutil.h (#3085) -- Mike Giroux Fri, 29 Jan 2021 12:51:05 -0500 @@ -106,6 +525,12 @@ bsl-internal (3.72.0.0) git; urgency=low * 489e70a15 Changelog for version 3.72.0.0 * 68b0e2781 Bumped version to 3.72.0.0 + * ebcc16635 Refactor bsl::function into smaller, well-documented and tested components (#3081) + * 20e2d72f8 Sunternary drqs 164912552 (#3077) + * 1835ace27 Async fileobserver threadsafety drqs 164688087 (#3055) + * 07e640af9 Work around Sun CC miscompilation error. (#3075) + * 9e3ff0d79 Do not try to use cpuid on sun (#3076) + * 82b5f7006 add comment regarding pre-fetch and false sharing (#3074) * 3581e44ae Merge pull request #3073 from mgill83/balxml-hexparser-docs-drqs-164301919 * 67829fbfc Merge pull request #2993 from bchapman/bsltf-dtors-sabotage-value-drqs-164353757 @@ -114,7 +539,12 @@ bsl-internal (3.72.0.0) git; urgency=low bsl-internal (3.71.0.0) git; urgency=low * a005dcd9b Changelog for version 3.71.0.0 + * e48039625 Fix use of string_view iterators in C++17 mode on windows (#3072) * 312c1fc05 Bumped version to 3.71.0.0 + * 35b0e0a87 Ball log attributes. (#3009) + * 96c92f83e bdlcc_skiplist: change 'updateR' and 'findR' to behave intuitively in… (#3068) + * 4f3a0d33a Handle encoding inf/nan values (#3066) + * 1affdcffa Clarify usability scope of bslmt::ThreadUtil::self() handle. (#3067) * 88cc3e097 Merge pull request #3063 from mgiroux/bde-baljsn_datumutil-windows-test-failures-drqs164717866 -- Mike Giroux Fri, 8 Jan 2021 15:14:47 -0500 @@ -123,6 +553,8 @@ bsl-internal (3.70.0.0) git; urgency=low * 1b940bb56 Changelog for version 3.70.0.0 * cd5b68390 Bumped version to 3.70.0.0 + * 262197eb2 0-sized buffers are allowed to be appended to 'bdlbb::Blob' (#2989) + * 46418fd94 DRQS 163966645: Apply decimal digit options to attributes in XML encoder (#3056) * b2f779068 Merge pull request #3057 from osubbotin/review/fix-stream-observer -- Mike Giroux Thu, 24 Dec 2020 11:19:40 -0500 @@ -131,14 +563,28 @@ bsl-internal (3.69.0.0) git; urgency=low * 06df42f3b Changelog for version 3.69.0.0 * 46373f997 Bumped version to 3.69.0.0 + * f29d39770 Minor doc tweaks. (#3053) + * 1e6768fe8 Make ball::StreamObserver configurable (#3032) + * b27954dcb add fsync/FlushFileBuffers to growFile (#3044) + * 903ca5ea4 addressing issue in DRQS 164468793 (#3043) * 9aa7acbdc Merge pull request #3050 from jberne4/legacy_attribute_noreturn + * 462bc6508 Fix ManagedDatum copy and Datum clone docs. (#3051) * b4105b072 Merge pull request #3049 from mclow3/removeStackCompare + * 0a5b77c6d add Intel-only caveats to "flat hash" doc (DRQS 164695777) (#3047) + * b22befc17 improve performance test (#3042) + * 4ffc2e9f2 Integration/flathashmap 20201112 (#3039) + * 94efd4991 replacing deprecated use of BSLS_KEYWORD_CONSTEXPR_RELAXED with BSLS_KEYWORD_CONSTEXPR_CPP14 (#3016) + * 5ca55320c 'Blob::d_dataIndex' field has the default value equal to '-1' (#3029) + * 53419b200 DRQS 164569283: enhanced exception trace to differentiate different use (#3020) + * 4446c51e3 Introduce PCG-based random number generator to GuidUtil (#2988) + * 54feb63d9 correct 'waitUntilEmpty' behavior (#3018) -- Mike Giroux Fri, 18 Dec 2020 11:19:32 -0500 bsl-internal (3.68.0.1) git; urgency=low * 5db54b356 Changelog for version 3.68.0.1 + * d60a19476 Revert "Update reference_wrapper to alias std (#2985)" (#3022) * c5f3d02b8 Merge pull request #3023 from cwilson/bsls_libraryfeatures-fix-typo -- Mike Giroux Fri, 4 Dec 2020 11:02:17 -0500 @@ -147,8 +593,15 @@ bsl-internal (3.68.0.0) git; urgency=low * eb29fb202 Changelog for version 3.68.0.0 * 829b63316 Bumped version to 3.68.0.0 + * a9f748e8d bslstl_charconv.t.cpp: prevent undefined behavior on LLONG_MIN (#3014) + * b7e0486c6 Drqs 160661580/bdlcc singleconsumerqueue concurrent allocation (#3010) + * 05365b554 Bdlcc skiplist no const char star drqs 164519609 (#3017) + * 863e77a98 bdlde_base64[en,de]coder: Add fuzz test (#3006) * 11b7e5f50 Merge pull request #3013 from mgiroux/remove-commented-error-directive + * 7bc277b5e bdlcc_skiplist: greatly enhance test driver, fix a couple of bugs (#2995) * f59b0b95d define link-coercion symbol per C++ library version (DRQS 164353299) (#2998) + * cf0d7041f clang 10 warning fixes have been reapplied for bslstl iterators (#3004) + * 9e33de4bb Update reference_wrapper to alias std (#2985) * a75d802c4 Merge pull request #3002 from mgiroux/revert-iterator-copy-ctor-changes-drqs157823946 -- Mike Giroux Fri, 4 Dec 2020 10:14:02 -0500 @@ -158,9 +611,12 @@ bsl-internal (3.67.0.0) git; urgency=low * 3f293edc0 Changelog for version 3.67.0.0 * da03213fd Bumped version to 3.67.0.0 * 4300d9681 Merge pull request #2999 from jdevenpo/xlc16-sljit-platform-detection + * 8caef4031 DRQS 164033256: Add fd support for getLastModTime in FS-Util. (#2945) * e3f4b0b35 Merge pull request #2980 from dsokolov7/review/bde-implicit-copy-constructor-drqs-157823946 * 59fe5fd5f Merge pull request #2996 from hrosen4/inteldfp_thread-drqs_164390534 * 28fa3efc4 Merge pull request #2990 from cwilson/bslstl_stringref-guard-explicit-instantiation-drqs-164345695 + * c75ecc780 Fuzz testing sample implementations (#2931) + * 32711b5cf Silence compiler warning for memcpy of bitwise movable objects. (#2987) * b0b5b1fc7 Merge pull request #2983 from dsokolov7/review/bde-int-max-sized-buffers-drqs-157652377 -- Mike Giroux Fri, 13 Nov 2020 15:06:44 -0500 @@ -169,10 +625,17 @@ bsl-internal (3.66.0.0) git; urgency=low * 7c80b47e6 Changelog for version 3.66.0.0 * cccbaa1d0 Bumped version to 3.66.0.0 + * 22a7954a1 bslmt,bdlcc,bdlmt: Update doc on bdlt::CurrentTime, bsls::SystemTime,… (#2978) * cb519c8d8 Merge pull request #2984 from bde/revert-2974-bslma_constructionutil-destructiveMove + * 3270fbfaa Fix 'destructiveMove' to use real moves in C++03 (#2974) + * 10814f02b Check for 0-sized buffers in 'appendDataBuffer' has been modified (#2982) + * 494569309 Drqs 164142056/bslh fibonaccibadhashwrapper creation (#2975) * 69218c22d Safe versions of buffer insert functions have been added to 'bdlbb:Blob' (#2937) * 56a8e2ba0 Merge pull request #2964 from mgiroux/apply-sim_cpp11-drqs161239121 + * a39095c57 Bde chrono to time interval drqs 150484626 (#2928) + * 5cf22b2c4 Problem with 'string_view' iterator on MSVC 2019 has been gotten around (#2972) * 41a77e4c9 Merge pull request #2976 from rkhlebnikov/drqs-164239441-fix-memcmp-ub + * 0da3028f7 Add make utility to bslma::constructionutil (#2947) * 2dec39c5b Merge pull request #2891 from mclow3/drqs-131875306 -- Mike Giroux Fri, 30 Oct 2020 17:17:16 -0400 @@ -182,10 +645,17 @@ bsl-internal (3.65.0.0) git; urgency=low * abf2afd23 Changelog for version 3.65.0.0 * 6d09952d3 Bumped version to 3.65.0.0 * 3899fabdb Merge pull request #2956 from phalpern/bsls_asserttest-cpp11_sim + * a987bc918 Remove blacklist.txt, wscript, and opts files no longer needed (#2970) * 8ebb30417 Merge pull request #7 from mgiroux/bsls_asserttest-cpp11_sim * 4cc83be3c Merge pull request #2967 from pwainwri/swt-deprecation * 8a4c431bb Merge pull request #2959 from bchapman/balxml_configschema-regenerate-drqs-163748896 + * cd9594ca6 Avoid unnecessarily prblematic words. (#2963) + * 90fefbf19 Implemented traits in 'MovableRefUtil' (DRQS 162823126) (#2911) + * a21168e40 Fix for DRQS 160192710: (#2941) + * b90a0ad04 Get rid of -cc parameter referencing swt (#2961) + * a0722e398 Warnings for enum bitwise operations have been fixed (#2936) * 81e73ce71 Merge pull request #2948 from phalpern/phalpern/guaranteed_copy_elision + * a13bb0a57 Implement fix to 'MovableRefUtil::access' (DRQS 162811827)' (#2910) -- Mike Giroux Sat, 17 Oct 2020 12:46:21 -0400 @@ -193,6 +663,9 @@ bsl-internal (3.64.0.0) git; urgency=low * 6b16c2029 Changelog for version 3.64.0.0 * 4e28c4e9b Bumped version to 3.64.0.0 + * 4657ccd84 Switch to more inclusive language than WHITELIST (#2950) + * b7a2bda74 Use new tokenizer method name (#2949) + * ec34b134c add JSON validation cases (#2935) * 9e46559c2 Merge pull request #2946 from bvaldiviesom/feature/add-bdls-pathutil-getextension * 1eb1a56f3 Merge pull request #2808 from mclow3/drqs-113474532 @@ -202,13 +675,28 @@ bsl-internal (3.63.0.0) git; urgency=low * 547cf08da Changelog for version 3.63.0.0 * 2d8eb0954 Bumped version to 3.63.0.0 + * 1e13432a3 fixing unused variable warnings with anone ufid (#2933) + * 9c03f0be6 Bdlt fixutil fuzz drqs 163929029 (#2934) + * 7e0c5b4ff DRQS 153564520: Make `baljsn_encoder` fail on stream buffer error (#2883) + * ec442cf5d 'bdlb_stringviewutil' has been added (#2927) * 4b5c854b6 BSLS_ASSERT_LEVEL_ASSUME* and constexpr BSLS_ASSERT/BSLS_REVIEW initial version * cdd237c9e Bumped version to 3.62.0.0 + * 9d36776ee Warnings from 'bslmf_matcharithmetictype' have been removed (#2930) + * d66f99da6 DRQS 163139285: Add file descriptor support for 'bdls::FilesystemUtil::getfileSize' (#2919) * ff4f1b9f6 Merge pull request #2929 from osubbotin/review/add-ufids-drqs-163623970 * 5d3203978 Merge pull request #2926 from mmaguir1/mmaguir1_DRQS_163343501_windows_warning_C4800 * 59ac30f4d Merge pull request #2879 from mclow3/drqs-118577170 + * 772ad3ab8 Fix Windows and fuzzing problems (#2924) * aa1c93633 Merge pull request #2922 from hrosen4/balxml_minireader-too-much-allocation + * 76a52b0fd Beware of too much reallocation blowing out sequential allocators (#2921) + * 9d6ea8446 Use data() instead of begin() (#2918) + * 66bf46159 correct Windows SSE macros (#2917) * 4c8715d45 Remove deprecated conversions from baltzo_zoneinfo. (#2892) + * c840428ef Warnings for enum arithmetic have been fixed. DRQS 161543055 (#2897) + * c49fdf3f2 Clarify treatment of the 'valudateInputIsUtf8' option (#2916) + * e4c1f6c91 baljsn_tokenizer: allowNonUtf8[Tokens -> StringLiterals] (#2898) + * 84d33c7f1 add SSE macros (#2915) + * ddc04cba0 update library features documentation (#2914) -- Mike Giroux Fri, 18 Sep 2020 12:21:46 -0400 @@ -216,9 +704,30 @@ bsl-internal (3.61.0.0) git; urgency=low * a8e484bcb Changelog for version 3.61.0.0 * ad483fd26 Bumped version to 3.61.0.0 + * 625fc2ae2 Fix typo in stringbuff.t (#2913) + * 629f29bea fix stringbuf move-assignment operator and str() method (#2912) + * e96358245 New style AA changes. (#2899) + * 820a4a146 Baljsn parserutil numeric parse util (#2909) + * 99d3aec12 Don't parse \u literals with strtol. (#2906) + * 7ee016538 Avoid AIX compiler error. (#2903) + * 67bb3d379 Don't rely on compiler negative 0. (#2904) + * 099f28b26 Get rid of silly test driver file reading. (#2905) + * db8edd1d1 Incomplete and incorrect checks. (#2907) + * 0e015ee5b Bad tables and hex checking. (#2908) * 3086bd9fd Merge pull request #2902 from mgiroux/add-missing-initializer-baljsn_datumutil.t.cpp + * a2664f326 Unsafe string test. (#2896) * 316dfa919 Merge pull request #2801 from mgiroux/document-duplicated-key-behavior-for-baljsn_datumutil-drqs159746247 + * 297577fa9 Cosmetically improve `balber_berutil` (#2887) + * 1e205d500 Balxml minireader drqs 154828363 (#2900) + * 0fcfd3be8 Berutil decimal64 drqs 162368178 (#2895) + * 78b6e3d0b Baljsn underflow drqs 162368278 (#2894) + * 218ace2d2 Refactor `baljsn_encoder` test driver (#2882) + * 9c34a61ed Balsjn datumutil depth limits drqs160193845 (#2821) + * 5786780d8 'swap' method has been added for 'ostringstream' and 'streambuf' (#2827) * 4f1d99efb Merge pull request #2890 from hrosen4/swapbytes-ub_drqs-162500976 + * 900602f96 Documentation for 'PooledBlobBufferFactory' has been updated. DRQS 161384667 (#2889) + * b235678d5 improve bslmt_lockguard, etc. function-level doc (DRQS 161549065) (#2849) + * 1fb100814 Update bslstl_stringview.h (#2888) -- Mike Giroux Fri, 21 Aug 2020 15:52:22 -0400 @@ -234,11 +743,17 @@ bsl-internal (3.60.0.0) git; urgency=low * c9ccd8cde Changelog for version 3.60.0.0 * 6cf4f67f4 Bumped version to 3.60.0.0 + * 875748758 update vector and array docs (#2874) * 643c1e1ad Merge pull request #2873 from osubbotin/review/remove-obsolete * 8c2442898 Merge pull request #2870 from afeher/drqs-161634136-newly-split-bind-test-driver + * 7f92e49d0 increase log message buffer size (#2872) + * 59ef1a1ce bslmt_testutil: Complete overhaul of guard macros, huge improvement (#2480) * 6ac2f79e4 Merge pull request #2865 from afeher/drqs-161457836-newly-split-list-test-driver + * 2f44521b5 bdls_filesystemutil.h: doc-only: fix inaccurate 'findMatchingPaths' doc (#2871) * a771df7cc Merge pull request #2864 from afeher/drqs-161365580-newly-split-vector-test-driver * 2c79f04f4 Merge pull request #2862 from afeher/drqs-161365350-newly-split-deque-test-driver + * 68c74f5e5 Null terminate string. (#2867) + * ab272e9eb 'linkcoercion' check has been added to 'bslstl_stringview' (#2866) * 169c9c4fe Increase minimum required cmake version to 3.15. (#2861) -- Mike Giroux Fri, 7 Aug 2020 11:22:00 -0400 @@ -247,12 +762,19 @@ bsl-internal (3.59.0.0) git; urgency=low * 9609964c9 Changelog for version 3.59.0.0 * 0b0dafcac Bumped version to 3.59.0.0 + * baf8289bb Fix build after merging PR 2789 (#2850) + * f0ca87804 drqs-161339530: Fix wording of 'bdlb_nullopt.h' documentation (#2831) * b8cad69a9 Merge pull request #2841 from mgiroux/PR-2666-latest * 6adc7449f Merge pull request #2789 from bchapman/bal-jsn,xml-decoder-check-utf8-drqs-158055467 * b0f374c3a Merge pull request #2847 from mgiroux/remove-smallsequential-allocator-and-pool + * 350cdba70 Bdlma smallsequentialallocator drqs 158144690 (#2840) + * 82a44feea Demote setlocale check to safe mode. (#2839) * e7248b29a Clean up string_view conversion tests * e44df7712 Enable more conversions and test them. * 9f0f58bbf String view conversions + * 2979daa51 'data' manipulator has been added to 'bslstl::string' (#2816) + * c550dff94 'MACROS' field has been added to 'ball_log' documentation (#2835) + * 436b804ea bdlde_utf8util: deprecate and rename problematic function names (#2818) * de8eb1b9e Merge pull request #2832 from APILayer/fix/baljsn-ub-clauses -- Mike Giroux Sat, 25 Jul 2020 16:47:53 -0400 @@ -263,12 +785,18 @@ bsl-internal (3.58.0.0) git; urgency=low * 2c6de7b55 Bumped version to 3.58.0.0 * 5749c2d59 Merge pull request #2819 from bchapman/bdlb_variant-createInPlace-return-drqs-160662990 * f77eb979d Merge pull request #2817 from bchapman/bslma_testallocatormonitor-fix-allocate-0-drqs-160613942 + * 297ed8e89 Revert "Change baltzo_localtimedescriptor to use new-style AA interface (#2795)" (#2829) + * d08af640b Change baltzo_localtimedescriptor to use new-style AA interface (#2795) * 31345c3d2 Merge pull request #2826 from afeher/drqs-159234337-melt-bslmt_threadattributes-interface * 0808aadd2 Merge pull request #2824 from sbreitst/bdlma_smallsequentialpool-drqs-160588397 * 33dd75890 Merge pull request #2811 from mclow3/drqs-126021517 + * c3c867830 DRQS 160155173 test case (#2809) * e90ca5012 Merge pull request #2820 from osubbotin/review/bdlb-tokenizer-compile-warnings * a9059d9fd Merge pull request #2815 from mclow3/drqs-113474532-deletions + * 7f02d866c drqs-154126434: fix makeDecimalRaw doc (#2810) * cf99ec112 Merge pull request #2813 from gsoumar/patch-1 + * 2a57ad95d Remove bde legacy names and typos. (#2812) + * 3dfdf90f0 updating to use sim_cpp11 properly and do some actual move-only testing on legacy platforms (#2807) -- Mike Giroux Fri, 10 Jul 2020 13:07:32 -0400 @@ -289,6 +817,7 @@ bsl-internal (3.56.0.0) git; urgency=low * 86e6f3ae5 Merge pull request #2802 from cteubner/patch-2 * e002c171f Merge pull request #2804 from cteubner/patch-4 * e992ffae7 Merge pull request #2799 from cwilson/bslstl_ratio-testing-drqs-151126457 + * d1339f4c8 Hash append for std drqs 159043737 (#2798) * 20fb14399 Merge pull request #2797 from ameredit/sun_warnings * 3c05e2d73 Merge pull request #2794 from jberne4/aligningallocator_update @@ -300,6 +829,9 @@ bsl-internal (3.55.0.0) git; urgency=low * 0227da956 Bumped version to 3.55.0.0 * 8eb2e365a Merge pull request #2787 from afeher/drqs-158600501-gcc9-WG14-N2322-recommendation-for-__LINE__-support * 870a353d7 Merge pull request #2792 from cteubner/patch-1 + * 2a213e2c7 DRQS 153917503: Provide Extended-Binary BER Date/Time Format (#2790) + * 881e550c2 ConstructionUtil: Add Coverity suspicious_sizeof (#2788) + * c9f2e1930 baljsn_encoder: Decrease template nesting to fix sun build (#2786) * af85e9428 Merge pull request #2784 from sbreitst/bdlma_infrequentdeleteblocklist-drqs-158142905 -- Mike Giroux Fri, 15 May 2020 10:51:01 -0400 @@ -308,7 +840,10 @@ bsl-internal (3.54.0.0) git; urgency=low * 89113474e Changelog for version 3.54.0.0 * 5143fccc3 Bumped version to 3.54.0.0 + * 5c63a777e Integrate JSON Comma Fix for DRQS 117361294 (#2783) * 2acca696b Merge pull request #2780 from rkhlebnikov/drqs-158146615-striped-containers-with-noncopyable-value-type + * 8ec94509c Bslmt doc various rw locks drqs 157529643 (#2767) + * 176af612e bdlcc_deque.h: typo in doc of 'clock type' (#2781) -- Mike Giroux Fri, 1 May 2020 10:52:26 -0400 @@ -316,16 +851,28 @@ bsl-internal (3.53.0.0) git; urgency=low * 6c42d15d9 Changelog for version 3.53.0.0 * 0739bbf45 Bumped version to 3.53.0.0 + * a4aa95bda Force undef BDE_OMIT_INTERNAL_DEPRECATED. (#2770) * 8927efb57 Merge pull request #2779 from afeher/drqs-157961303-fix-map-test-drivers-sun + * ab0167164 Remove erroneous typedef - DRQS 157960535 (#2776) * 896dbff7d Merge pull request #2777 from afeher/drqs-157960635-fix-multiset-test-on-sun * a670b8723 Merge pull request #2775 from afeher/drqs-157952290-fix-multimap-test-on-sun + * 3f5e62d65 bdlbb::BlobUtil::alignTo: create function (#2762) * db04bdf6b Merge pull request #2768 from osubbotin/review/omit-deprecated * c400c0329 Merge pull request #2774 from afeher/drqs-157937050-fix-hastable-test-on-sun * 83c419a46 Merge pull request #2773 from afeher/drqs-157900947-fix-deque_test-on-sun * 87ee1479d Merge pull request #2772 from afeher/drqs-157856446-split-vector-test-drivers-SUN + * 17901a878 Baltst create package drqs 155898178 (#2748) + * 93939dfaa Cleanup ball TDs from deprecated API. (#2771) + * 0c6307b86 Add 32 bit PIC-enabled builds of thirdparty libraries. (#2769) + * a6dd3ca0f bdlbb_blobutil.h: fix typo for BlobUtilAsciiDumper (#2765) * 4c7ff7ff0 Merge pull request #2764 from afeher/gcc-on-darwin-code-fixes + * 859b03f29 Use non-deprecated enum value. (#2761) * 648d13a1c balcl_option.h: 'bdedox' conversion fix: doc-only + * f00fa8877 drqs-157516821: 'balcl_option.t.cpp': MSVC 2017/2019 build warnings. (#2751) + * 9bdd77d64 Fix coding style in bslstl_sharedptr.h (#2753) + * 4192393b9 Fix Doxygen warnings in bslstl_sharedptr.h (#2752) * 497d6106e Merge pull request #2749 from bchapman/balst-open-source-drqs-147852908 + * 322315df7 Balcl commandline drqs 155623712 Phase Six (#2741) -- Mike Giroux Fri, 24 Apr 2020 11:35:08 -0400 @@ -333,14 +880,19 @@ bsl-internal (3.52.0.0) git; urgency=low * 10f9c910f Changelog for version 3.52.0.0 * b72bb6053 Bumped version to 3.52.0.0 + * e367073d3 Bsls dbghelpddlimpl windows thread safe drqs 155085199 (#2707) * f91d32f72 Merge pull request #2746 from afeher/fix-bsls_ident-undef * c469c4d30 Merge pull request #2744 from afeher/fix-smartptr-test-drivers * 94d7dd276 Merge pull request #2743 from mgiroux/sean-baxter-github-pr-256 + * 93e91d2e4 Bdld datum doc maps drqs 154585376 (#2738) * 2b3d07323 Merge pull request #2740 from afeher/drqs-155897793-copy_if-for-cpp03 * e9ab538d8 Merge pull request #2735 from afeher/remove-bsls_ident-undef * f6c7855ff Merge pull request #2739 from afeher/drqs-156777393-remove-bsl_cinttypes-abs-div + * 55138e7b9 bslma_testallocator: doc balst::StackTraceTestAllocator (#2731) * 638e3c5c7 Merge pull request #2728 from afeher/drqs-142188247-assign-shared_ptr-from-managedptr * 18686f3d3 Merge pull request #2733 from sbreitst/balcl_option-drqs-156728040 + * 12a599b18 Add optional ...Args to _constructible_v. (#2730) + * 55cdcf4e0 Balcl option drqs 155623667: Phase Five (#2718) -- Mike Giroux Fri, 3 Apr 2020 11:17:14 -0400 @@ -348,14 +900,20 @@ bsl-internal (3.51.0.0) git; urgency=low * a51bd84c2 Changelog for version 3.51.0.0 * b9d6e3d61 Bumped version to 3.51.0.0 + * 7944f8179 Fix pair on gcc-7 & clang. (#2725) + * 07377d8bb drqs-156228570: 'balcl_typeinfo': TC 9 bug on 64-bit sparc. (#2724) * 7f1b9acea Merge pull request #2723 from cwilson/fix-string-ctor-doc-drqs-156319171 * 8b3c0ccf3 Merge pull request #2722 from egoodrich4/improve-datum-clone * 0823e852a Merge pull request #2720 from afeher/fix-pair-test-driver * f3a748bc0 Merge pull request #2719 from afeher/drqs-156229760-cpp20-enum-warning + * fa13db7a0 Fix invalid syntax. (#2716) * 629561964 Merge pull request #2715 from jmendelsohn4/drqs-156214645/bdlmt_multiqueuethreadpool-case-33-threshold * c1f14b479 Merge pull request #2714 from jmendelsohn4/drqs-156182592/bdlma_concurrentpoolallocator-case-6-20200303 * 26ed4566d Merge pull request #2711 from jmendelsohn4/drqs-156161832/bdlcc_objectcatalog-clang-cleanup-20200303 * 8fd61c8d2 Merge pull request #2685 from bchapman/balxml-Decimal64-decimal-mode-drqs-154624542 + * 60ee87974 Behavior of 'bslim::Printer' functions using user's functors has been fixed (#2692) + * c1bdceaea Balcl commandline drqs 154845275: Phase Four (#2698) + * 003c12456 Drqs 143470677/bdlma concurrentpoolallocator crash on big allocate (#2709) * 413e7538a Merge pull request #2708 from jmendelsohn4/drqs-135423849/bdlma_sequentialallocator-incorrect-maxBufferSize-2 -- Mike Giroux Fri, 13 Mar 2020 11:35:37 -0400 @@ -370,8 +928,10 @@ bsl-internal (3.50.0.0) git; urgency=low * 02b7bc30c Merge pull request #2703 from jmendelsohn4/drqs-155286712/bdlmt_timereventscheduler-tsan-20200221 * cb438ef9a Merge pull request #2700 from jmendelsohn4/drqs-155641810/bsls_spinlock-revert-backoff * 9e11a28de Merge pull request #2691 from cwilson/nullablevalue-free-swap-allow-differing-allocators-drqs-123088904 + * 6c01e4ba1 Remove libbos-dev and stop building bos with cmake (#2695) * a853d636a Merge pull request #2673 from bchapman/bsltf_wellbehavedmoveonlyalloctesttype-2-drqs-129710603 * 027a699fe Merge pull request #2694 from hrosen4/bid-win_drqs-155595047 + * e217fbcb4 'balcl': Phase Three (drqs-154964494) (#2688) -- Mike Giroux Sat, 29 Feb 2020 17:53:22 -0500 @@ -379,6 +939,9 @@ bsl-internal (3.49.0.0) git; urgency=low * 302150da1 Changelog for version 3.49.0.0 * 3dd06cc37 Bumped version to 3.49.0.0 + * 88a41b44b Fix documentation typos (#2693) + * ebd447154 Add option to configure thread attributes to 'balb::PipeControlChannel' (#2680) + * 456947c93 'bsl::exception' declaration in bsl_ios.h DRQS 154479723 (#2683) * 9b6aa35f8 Merge pull request #2689 from jmendelsohn4/drqs-155023497/bdlcc_stripedunorderedmap-erase * b2bca2de4 Merge pull request #2690 from jberne4/review_windows_nostack @@ -389,9 +952,12 @@ bsl-internal (3.48.1.0) git; urgency=low * b40f8fea5 Changelog for version 3.48.1.0 * a3ac77bde Bumped version to 3.48.1.0 * c110a114e Merge pull request #2684 from hrosen4/dfp-format_drqs-154919352 + * 0a08c84c4 drqs-154833435: 'balcl': Phase Two (#2678) * 858a954b2 Merge pull request #2682 from hrosen4/dfp-format_drqs-154874861 * c5754e515 Merge pull request #2677 from bde/review/force-disable-cpp17-abi * d1448a48f Merge pull request #2679 from jmendelsohn4/drqs-154832051/bdlmt_multiqueuethreadpool-case-12 + * ae9d7a5f9 Make all exponents at least two digits and implement showpoint. (#2631) + * 673136a23 Fix bslim_testutil.t failure on gcc 9 (#2674) * d93945ed6 Merge pull request #2548 from bchapman/bdlcc_objectcatalog-move-semantics-drqs-149082155 -- Mike Giroux Fri, 31 Jan 2020 16:14:28 -0500 @@ -401,6 +967,7 @@ bsl-internal (3.48.0.0) git; urgency=low * 6ddc6633f Changelog for version 3.48.0.0 * d4038d2ec Bumped version to 3.48.0.0 * 2f7f16c25 Merge pull request #2665 from jmendelsohn4/drqs-153332608/bdlcc_boundedqueue-race-waitUntilEmpty-4 + * 6211ad2d8 Disable cpp17 ABI flag. (#2675) -- Mike Giroux Sun, 26 Jan 2020 08:18:57 -0500 @@ -408,6 +975,7 @@ bsl-internal (3.47.0.0) git; urgency=low * a0a4e1c8c Changelog for version 3.47.0.0 * 18f8e138c Bumped version to 3.47.0.0 + * 281d46b2d Initial implementation of balcl commandline (#2563) * c3e61e15c Merge pull request #2672 from rkhlebnikov/drqs-154432480-fix-stringview-merge-issue * 3a59e6444 Merge pull request #2662 from hrosen4/string-view-17 * a5d6a3c36 Merge pull request #2641 from nburgers/191118-balber-DRQS-139634693-TESTS @@ -418,7 +986,9 @@ bsl-internal (3.47.0.0) git; urgency=low * f79bb9b46 changing constexpr macros to name language version * 2ce460438 Merge pull request #2668 from afeher/fix-bslim_bslstandardheadertest-case-7-for-msvc * 29a5e8849 Merge pull request #2667 from mgiroux/fix-istriviallycopyable-msvc-2013-drqs153989414 + * bb8d2fb21 assertimputil should not be included if assert is. (#2664) * 10167d9d8 Merge pull request #2663 from mgiroux/pr/2661-rebased-master + * 01f878d64 Add missing bsls_platform.h. (#2655) * 6d1ac3ea9 Revert "Merge pull request #2640 from ameredit/detect_nested_traits_no_sun_abomination" * d6e0bf090 Fix string view code for compilation as alias to native version. * 5d9bd9adf Merge pull request #2645 from dsokolov7/review/bde-assertiontracker-removal-drqs-152681578 @@ -441,13 +1011,16 @@ bsl-internal (3.46.0.0) git; urgency=low * 3366afb62 Changelog for version 3.46.0.0 * d5d8e2955 Bumped version to 3.46.0.0 * 3f96918dc Merge pull request #2652 from mgiroux/add-missing-includes-for-if-ifn-defs-drqs153708911 + * dfa189ef4 Multiple-valued trnsparent comparators support has been added (#2613) * 4d4e964ac Merge pull request #2648 from mgiroux/suppress-maybe-uninit-balxml_encoder-drqs153551134 * ceb31bd1c Merge pull request #2649 from mgiroux/pr/2591-rebased-master + * a7199b3b7 Fix bad inline variables. (#2643) * 98ca63085 Merge pull request #2640 from ameredit/detect_nested_traits_no_sun_abomination * a28735f22 Merge pull request #2644 from hrosen4/bad-string-hash_drqs-153467238 * ad96a9143 Merge pull request #2642 from hrosen4/bad-fenv_drqs-153457037 * e3fb70d37 Merge pull request #2630 from afeher/drqs-153145297-fix-msvc-2017-out-of-memory * d9f5ec454 Merge pull request #2626 from ameredit/fix_isnothrow_move_constructible_for_gcc03 + * 8ef924a5f String view sun (#2594) * 2d53e7a2f Merge pull request #2628 from dsokolov7/review/bde-decimal-bid-conversion-drqs-151660179 * 69505a652 Merge pull request #2636 from hrosen4/striped-custom-equal_drqs-153304888 * 51129a3bd Merge pull request #2570 from bchapman/bsltf-move-noexcept-drqs-150993980 @@ -540,10 +1113,12 @@ bsl-internal (3.42.0.0) git; urgency=low * fc79841b1 Merge pull request #2553 from jberne4/remove_libbos_dev_deps * 7d0b586ca Merge pull request #2559 from mgiroux/add-Conflicts-to-debian-control * fb6cc82c8 Merge pull request #2558 from mgiroux/baljsn-doxygen-fixes-3.41.0 + * e6e1c3186 Fix documentation errors. (#2557) * 84b408c1e Merge pull request #2537 from ameredit/optimizing_voidtype_for_compilation * 638801e91 Merge pull request #2533 from nburgers/191009-balxml-decoder-DRQS-146705251-2-RB * c4e636b9d Merge pull request #2536 from nburgers/191018-baljsn-DRQS-146678827-RB * d4c7694b8 Merge pull request #2546 from nburgers/191024-bslmt-once-DRQS-149823688 + * bea2c2ea4 Documentation typos. (#2549) -- Mike Giroux Fri, 8 Nov 2019 12:00:21 -0500 @@ -557,6 +1132,7 @@ bsl-internal (3.41.0.0) git; urgency=low * 5f7ad197a Merge pull request #2542 from glaptev1/update_bdldfp_decimal_doc * d56560ace Merge pull request #2544 from mgiroux/remove-trailing-spaces * 9daf0a937 Merge pull request #2543 from ameredit/whitespace_fixes_for_description + * 14d413fb3 Add /bigobj flag for balxml test drivers. (#2540) * a1e1f4dae Merge pull request #2539 from bchapman/bdlma_bufferedsequentialpool-fix-release-drqs-150407633 * 82ebc8e30 bdlma_bufferedsequentialpool: make 'release' realease ALL dynamic memory * 70ef09e91 Merge pull request #2538 from jmendelsohn4/drqs-150355963/bdlmt_eventscheduler-correct-doc-for-microseconds @@ -588,6 +1164,7 @@ bsl-internal (3.40.0.0) git; urgency=low * 7273c4da5 Changelog for version 3.40.0.0 * 7fe2360d2 Bump version to 3.40.0 + * 6f82ec281 Fix various typos accumulated in the documentation sections. (#2513) * a1f4f8445 Merge pull request #2506 from jberne4/noexcept_types * cd3ed276d Merge pull request #2500 from hrosen4/win-sp_drqs-148281696 * fb9740989 Merge pull request #2395 from ameredit/avoid_unnecessary_MetaInt_temp @@ -625,6 +1202,7 @@ bsl-internal (3.38.0.0) git; urgency=low * 0e256fcad Merge pull request #2494 from apaprock/managedptr-docs * 328f2abbb Merge pull request #2472 from sbreitst/searchers-drqs-142948762.4 * ff5560fc2 Merge pull request #2493 from bchapman/bsla_format-usage-scanf-drqs-147768587 + * df79b0f14 Define va_copy() for c++03 compilation mode. (#2490) * 9486434f3 Merge pull request #2489 from hversche/fix-C-linkage-issues-from-BSL_OVERRIDES_STD-drqs131017375 * 9a05899b7 Merge pull request #2460 from bchapman/bdls_filesystemutil-nightly-build-drqs-147189788 * 7cfff510b Merge pull request #2458 from sbreitst/RwMutexAssert-drqs-107102365 @@ -644,6 +1222,7 @@ bsl-internal (3.37.1.0) git; urgency=low * 04a94fe13 Changelog for version 3.37.1.0 * a0211436f Bump version to 3.37.1.0 * 745c795ad Merge pull request #2471 from osubbotin/review/ball-usage-update-drqs-147317724 + * 2cfd7b7c2 Drqs 147433439/bslmt conditionimpl win32 timeout underflow (#2473) * 4af7bce8c Revert "Merge pull request #2465 from bde/revert-2456-searchers-drqs-142948762.3" * e2d6ffb8b Merge pull request #2469 from osubbotin/review/oss-update-readme * 5f75c15b6 Merge pull request #2431 from osubbotin/review/oss-compilation-fix @@ -671,6 +1250,7 @@ bsl-internal (3.37.0.0) git; urgency=low * 8f709fc51 Merge pull request #2441 from ddragan/bslmf_util_moveifpossible * 34c7c2d32 Merge pull request #2451 from jmendelsohn4/drqs-146224969/bdlb_bitutil-add-msvc-intrinsics * bdcc7187d Merge pull request #2449 from bchapman/balst-doc-balst-txt-performance-drqs-145270921 + * a4d6f2b82 Searchers drqs 142948762.3 (#2445) * 418c92425 Merge pull request #2447 from oimanuel/fix_race_throughputbenchmark-drqs-146586504 -- Mike Giroux Fri, 16 Aug 2019 14:08:50 -0400 @@ -729,12 +1309,20 @@ bsl-internal (3.33.0.0) git; urgency=low * 3d353ad89 Changelog for version 3.33.0.0 * 2cf635531 Bump version to 3.33.0.0 + * b9081c2b3 Prevent 'function::swap' swapping incompatible functions (#2418) + * de9ee73fb Drqs 143578129/bdlmt multiqueuethreadpool num elements stress test (#2421) * 9f82bd753 Merge pull request #2420 from bchapman/bdlmt_signaler-nightly-build-cpp17-drqs-144867783 + * 88f8a9547 More MetaInt cleanup (#2396) + * 10b81e87d Correct is_nothrow_move_constructible trait (#2409) + * 2febf0e3f fix (#2416) * 745e6a19a Merge pull request #2361 from bchapman/bdlmt_signaler-create-component-drqs-127658315 + * c04b86347 Fix reorder warnings in test driver (#2415) * 7f71b1767 Merge pull request #2402 from oimanuel/fix_msvc_compile-drqs-143877762 + * ef58b03c7 fix (#2413) * 9662357cb Merge pull request #2412 from bde/fixup-doc-markup-3.32.0 * 5f9183d69 Merge pull request #2411 from bchapman/bsls_annotation-nightly-build-nodiscard-drqs-144396149 * 7ea798f81 Merge pull request #6 from ddragan/bdlmt_signaler_tests_refactor + * ec3d784e4 bslmt_throughputbenchmark TD failures DRQS 144393977 (#2410) -- Mike Giroux Fri, 5 Jul 2019 09:27:53 -0400 @@ -743,9 +1331,15 @@ bsl-internal (3.32.0.0) git; urgency=low * 027362ffe Changelog for version 3.32.0.0 * 1537ef20b Bump version to 3.32.0.0 * 5cf2805e2 Merge pull request #2271 from mgiroux/move-bsl+stdhdrs-to-bos + * 7112384a6 New Bslmt throughputbenchmark drqs 135123704 (#2401) * 05420f2a1 Merge pull request #2407 from jmendelsohn4/drqs-144273361/bdlcc-fix-intermittant-timing-failures * 8f344cf5a bdlmt_signaler: fix 'noexcept' errors in last version * 7f7a06822 Merge pull request #2305 from bchapman/bsla_annotation-above-bsls-drqs-134834126 + * bd1dd173a Drqs 143438145/bdlcc singleproducerqueue improve try pop front (#2406) + * c5fd23854 switch to windows native condition variables (#2404) + * 9609bcf2a sync to bdlcc_boundedqueue (#2403) + * 776fac19b Drqs 142952992/bdlcc boundedqueue creation (#2376) + * 9b5ebde51 Drqs 143192683/bdlcc singleproducersingleconsumerqueue creation (#2391) -- Mike Giroux Sun, 23 Jun 2019 12:14:07 -0400 @@ -757,6 +1351,7 @@ bsl-internal (3.31.0.0) git; urgency=low * 71a94ad6e Merge pull request #2399 from afeher/baltzo-exemplar-cleanup * 8bf498532 Merge pull request #2398 from osubbotin/review/scm-semicolons * 4c20c1ae8 Merge pull request #2397 from ameredit/fix_trailing_whitespace + * ef28309ca spinlock backoff implementation and performance test DRQS 141894256 (#2363) * fb69e6f64 Merge pull request #2394 from sbreitst/remove-createNode-drqs-141430548 * da4911e39 Merge pull request #2377 from bchapman/bdls_filesystemutil-nightly-build-TC-18-drqs-143013557 @@ -776,7 +1371,12 @@ bsl-internal (3.30.0.0) git; urgency=low * 3bfeb9c6a Merge pull request #2388 from mgiroux/create-empty-bos-pkg-grp * 4427f1aa2 Merge pull request #2386 from rkhlebnikov/drqs-143224375-array-swap-adl * a531a80d8 Merge pull request #2385 from ameredit/variadic_common_type_t + * c46084bf8 Make `encodeQuotedDecimal64` default true in `baljsn` (#2299) + * 6d4e7b604 add missing inline (#2384) * 43416a37e Merge pull request #2358 from ameredit/remove_bslmfispointertomember + * 6e84f87fa correct bug (#2378) + * 07d397845 correct compile warning (#2375) + * 2c9a0eef1 Drqs 140362544/bslmt fastpostsemaphore creation (#2346) * 8ec6ffead Merge pull request #2373 from jmendelsohn4/drqs-142827756/bdlmt_threadattributes-allocator-awareness * 2010b3321 Merge pull request #2369 from hversche/c++17-build-drqs114104661 * 6fb1e8363 Merge pull request #2370 from ameredit/remove_windows_platform_hacks @@ -789,6 +1389,7 @@ bsl-internal (3.29.0.0) git; urgency=low * 2bf0f169c Changelog for version 3.29.0.0 * 60bc56e94 Bump version to 3.29.0.0 + * d333aba50 add 'allocator' accesor, use 'allocator()' to create thread (#2367) * 2a3c626b2 Merge pull request #2340 from ddragan/perfect_forwarding_for_bslalg_constructorutil * ce9f0455d Merge pull request #2364 from apaprock/bslstl-list-default * daeb75cf9 Merge pull request #2355 from dbeer/bdls_memoryutil_tests @@ -801,12 +1402,16 @@ bsl-internal (3.28.0.0) git; urgency=low * 5c7fb08ff Changelog for version 3.28.0.0 * 93e003d2e Bump version to 3.28.0.0 * 5b3ca5bc7 Merge pull request #2242 from jberne4/drqs_135750941-migrate_reviews + * 0c40a1703 set close on exec for the files (#2362) + * 9c2519a2b Striped hash map without performance test (#2195) * 650e038ef Merge pull request #2359 from osubbotin/review/remove-authors + * 7e15847de Fix typo in BerDecoder error message. (#1947) * 3d3d78697 Merge pull request #2357 from jberne4/typo_fix-drqs_141167082 * d8c14fa6e Merge pull request #2350 from jberne4/assert_extern_filename_hack * 42a8f5803 Merge pull request #2055 from rtartler/build_depends * 5010cd1e5 Merge pull request #1701 from jdevenpo/qpc-reference * f784a872e Merge pull request #2349 from dbeer/support_gtest_extension + * 3bb126e79 Set default max sequence size to 1GB for BER decoder (#2307) -- Mike Giroux Fri, 3 May 2019 10:44:04 -0400 @@ -825,6 +1430,9 @@ bsl-internal (3.27.0.0) git; urgency=low * e093bca73 Merge pull request #2263 from hrosen4/system_error * e1f986d85 Merge pull request #2341 from afeher/drqs-140781258-rework-stlport-xxx_bound-fixes * 351c200ea Merge pull request #2323 from dsokolov7/review/bde-stringref-printto-drqs-139427922 + * 8488b794f Remove external header guards from bsl+stdhdrs (#2335) + * e9cde05d3 Resolve mismatched tags warning in array (#2336) + * cc310d272 Native_std forward list (#2210) -- Mike Giroux Sun, 21 Apr 2019 13:46:14 -0400 @@ -832,13 +1440,18 @@ bsl-internal (3.26.0.0) git; urgency=low * 0e0489f77 Changelog for version 3.26.0.0 * 32cbdc54f Bump version to 3.26.0.0 + * 44d9dcd50 Remove unneeded explicit template instantiations on IBM test driver (#2338) + * 2436d0085 Clean up basic bde_verify issues in bslstl test drivers (#2322) * 370d0c57e Merge pull request #2339 from hversche/remove-CLASS-SCOPE-drqs138311093 + * 6970ff4d3 Drqs 140403279/bdlmt multiqueuethreadpool pause create deadlock (#2333) * eda741ae5 Merge pull request #2337 from jmendelsohn4/drqs-140575133/bal-nb-20190405 + * 9ee3048da Adjustments to bslma_default (#2326) * d266882da Merge pull request #2288 from bchapman/bdls_processutil-processName-argv0-redone-drqs-122218213 * ee14ae853 Merge pull request #2310 from jberne4/DRQS_139414941-move_exemplar * b0322be88 Merge pull request #2334 from jmendelsohn4/drqs-140522902/bal-test-compile-warnings-20190404 * 8e9737f32 Merge pull request #2233 from bchapman/ball_loggercategoryutil-fix-hier-drqs-133568178 * 82a97254d Merge pull request #2330 from hversche/fix-stlport-drqs139734639 + * 1db8903e1 Address {DRQS 137104924} - BER Decoder empty string decoding (#2287) * 6e8b8f501 Merge pull request #2332 from ameredit/stlport_on_sun * b886bc08e Fix version-detect for CC with STLport library @@ -847,9 +1460,13 @@ bsl-internal (3.26.0.0) git; urgency=low bsl-internal (3.25.0.0) git; urgency=low * 5a779197e Changelog for version 3.25.0.0 + * ae79d24e9 fix resume too soon after pause fails (#2327) * 7fe905dea Merge pull request #2314 from osubbotin/review/remove-force-pic * fa52f65b1 Merge pull request #2325 from osubbotin/review/doxygen-fixes * 707a77892 Merge pull request #2329 from hrosen4/cygwin-endianess_drqs-140210218 + * e95a8439c correct compiler warning (#2328) + * 95c3a4a3e Conan build support. (#2089) + * 192052661 xlC does not support C++11 noreturn (#2324) * 85156d8a8 Merge pull request #1994 from fbirbacher/reduce-inline-code-for-default-allocator -- Mike Giroux Fri, 29 Mar 2019 15:09:02 -0400 @@ -867,13 +1484,17 @@ bsl-internal (3.24.0.0) git; urgency=low * 4fde10686 Changelog for version 3.24.0.0 * 6fefdcafd Bump version to 3.24.0.0 + * 281da0501 Drqs 138047258/bdlmt multiqueuethreadpool improve num processed (#2316) * 418e7907a Merge pull request #2311 from jmendelsohn4/drqs-139704002/bdlt_datetimeinterval-correct-cmath-include * ffcd52e6a Merge pull request #2308 from osubbotin/review/fix-namespace-category-drqs139634627 + * 4c4582385 Don't set the HAS_CPP11 macros in C++03 mode (#2315) * 8769e518a Merge pull request #2313 from cwilson/declare-blob-blobbuffer-move-ctors-noexcept-drqs-139707320 * 6d30aa998 Merge pull request #2254 from dsokolov7/review/bde-ameredit-array-drqs-131523442 * c83967474 Merge pull request #2282 from dsokolov7/review/bde-datum-print-drqs-137688398 * e8f6fcc7b Merge pull request #2306 from jmendelsohn4/drqs-139501501/bdl_nb_compile_warnings * f042f5753 Merge pull request #2300 from jberne4/DRQS_139017627-shared_ptr_unique_ptr + * 9b14c2547 correct synchronization of 'deleteQueueCb' (#2304) + * 01064b0ea correct possible deadlock while calling 'pauseQueue' (#2302) -- Mike Giroux Fri, 22 Mar 2019 14:28:56 -0400 @@ -881,8 +1502,12 @@ bsl-internal (3.23.1.0) git; urgency=low * 1dd0b03ba Changelog for version 3.23.1.0 * 44b44162f Bump version to 3.23.1 + * b20103723 improve doc (#2298) * 88de69196 Merge pull request #2297 from bde/undo-traits-changes-to-avoid-libsunir-issue + * 622e10208 change 'd_owned' from volatile to atomic (#2296) * 6743e4d7f Merge pull request #2295 from jmendelsohn4/drqs-138951857/bal-build-warnings + * 215d2b996 balst_stacktraceutil.t.cpp: compiler bug in Solaris CC couldn't handle TC 7 (#2294) + * abee8d385 make destructors default (#2293) -- Mike Giroux Fri, 8 Mar 2019 12:23:58 -0500 @@ -893,7 +1518,10 @@ bsl-internal (3.23.0.0) git; urgency=low * 5b035f46e Merge pull request #2270 from jberne4/drqs_137502208-assert_failure_log_level * 30158bcc0 Merge pull request #2291 from oimanuel/bslmf_if_deprecate-drqs-138614884 * cc07f8d30 Merge pull request #2289 from hversche/add-forward-as-reference + * 87bac1a56 Simplifying bslmf_voidtype for better use with C++11 (#2280) + * 4f5797ccd sync doc between bdlcc_single* components (#2284) * 2bf55d656 Merge pull request #2285 from bchapman/bdls_osutil-bsl_ostream_h-drqs-138288665 + * e8049a523 correct undefined behavior doc (#2283) * f357925b0 Merge pull request #2281 from hrosen4/datum-warning_drqs-138067316 -- Mike Giroux Mon, 4 Mar 2019 10:07:47 -0500 @@ -903,11 +1531,17 @@ bsl-internal (3.22.0.0) git; urgency=low * 75b5e09b7 Changelog for version 3.22.0.0 * 55563d105 Bump version to 3.22.0 * dad1065fc Merge pull request #2279 from apaprock/bdld-datum-opt-assert + * f3c5e91b8 Add 'bdlb::nullOpt' support to 'bdlb::NullableValue' (#2272) * e86f855e3 Merge pull request #2277 from bchapman/bdls_fdstream-read-past-buffer-end-drqs-137573159 + * 77b9683c0 balst_stacktraceresolverimpl_elf.cpp: enhance to resolve multiple segments (#2252) * 9574b3977 Merge pull request #2267 from bchapman/bslim_gtestutil-less-template-ostream-drqs-136959045 + * 2d90b30e7 Remove specific workarounds for old xlC compilers (#2276) * 68abafeef Merge pull request #2275 from ameredit/bde_verify_includes_for_bdl * 16f726348 Merge pull request #2248 from bchapman/bdls_osutil-VersionHelper-drqs-131942046 + * b37e282ad asserts fixed (#2268) + * dcd89539b Address bde_verify issues will 'bdlb_nullablevalue' (#2269) * 8e649ba02 Merge pull request #2262 from bchapman/bsls_dbghelpdllimpl_windows-night-build-fix-drqs-136944165 + * cfd9ec4e3 Adjust ~RWMutex documentatoin (#2266) * a7ca35132 Merge pull request #2260 from jberne4/drqs_136893894-bad_time_separate_functions * e42490a57 Merge pull request #2261 from ameredit/nullopt_t * 0cdb7ea20 Merge pull request #2265 from oimanuel/fix_bdlcc_cache_locking_test-drqs-137336541 @@ -926,16 +1560,26 @@ bsl-internal (3.20.0.0) git; urgency=low * 0e4422892 Changelog for version 3.20.0.0 * ce966b001 Bump version to 3.20.0 + * 89809fa56 Balst stacktraceresolverimpl xcoff gcc drqs 130613191 (#2255) * 9df57b53e Merge pull request #2259 from osubbotin/review/doxygen-fixes + * c712bbaef minor doc fixes (#2258) + * ae1a5074b Fix some compile errors when native traits support is disabled (#2256) + * ba7ff4df0 Resolve any build issues when transitive includes are disabled (#2257) * b007cf148 Merge pull request #2253 from osubbotin/review/file-set-category-drqs133952742 * 10fb3b480 Merge pull request #2083 from dsokolov7/review/bde-z-bael-deadlock-drqs-110401470 * 2c7c99cee Merge pull request #2229 from dsokolov7/review/bde-output-redirector-drqs-131523442 * b24b29e08 Merge pull request #2230 from bchapman/bdlcc-late-signaling-doc-drqs-135160731 * f7f1df2b5 Merge pull request #2223 from dsokolov7/review/bde-variant-references-drqs-111480299 * d574b324a Merge pull request #2249 from jberne4/drqs_136091384-fix_gcc_attributes + * 67ac2f862 Silence test driver warnings on spurious cv-qualifiers (#2250) * bd9a2e8a0 fixed versions where gcc declares nodiscard and maybe_unused available * 238349fd8 Merge pull request #2246 from cwilson/bsls_libraryfeatures-cpp14-clang-warning-drqs-135945454 + * e19680ee3 Fix 'bslstl_pair.h' warnings on Clang (#2247) * 8ee5b48ff Merge pull request #2245 from osubbotin/review/doxygen-fixes + * 9cd759201 bdls_processutil: fix nightly build failure in TC 1 on Windows (#2243) + * e58a5df2b DatetimeTz: Fix typo in constructor description (#2227) + * c18a53f41 Fix reference in docs to own package name (#2240) + * ac42870a8 Resolve many bslx warnings (#2241) * 3c6861e0c Merge pull request #2239 from jberne4/drqs_135435123_attribute_fix -- Mike Giroux Mon, 28 Jan 2019 12:29:26 -0500 @@ -945,12 +1589,27 @@ bsl-internal (3.19.0.0) git; urgency=low * 6e99e6c3d Changelog for version 3.19.0.0 * aeb354a28 Bump version objects to 3.19.0 * a02a3b6c0 Merge pull request #2238 from afeher/drqs-128628205-testallocator-alignment + * 800a06aa1 Balst advanced linux nightly build drqs 133554846 (#2202) + * cf0732eee Extern template for stringref (#2236) + * 2da377ec7 fix sign extension tests and getUint24 imp (#2237) * 432de5a90 Merge pull request #2212 from jberne4/use_annotations * 03ab26c47 Merge pull request #2228 from jberne4/review_fix + * 03b29d32c Typedef for "OnFileRotationCallback" in observers. (#2234) * 061d68882 Merge pull request #2175 from bchapman/ball_log-HIER-MACRO-drqs-129768889 * 34acc5a45 Merge pull request #2226 from bchapman/bdlcc_cache-bug-with-VALUE=shared_ptr-drqs-134930805 + * 6f91c0c66 make advanceTime wait for event scheduling (#2224) * 6c4400839 Merge pull request #2225 from osubbotin/review/update-synopses + * aa0571b31 fix hyphenation in bslma_usesbslmaallocator doc (DRQS 134796240) (#2222) + * 4c4f6e3a0 Clean up bslstl_hash test driver (#2221) * e24a8d9d3 Merge pull request #2183 from dsokolov7/review/bde-constructionutil-non-copyable-drqs-132281710 + * b102f869d enable SSE4.2 instructions in WAF build of BDLDE on Darwin (#2220) + * 77e830416 Fix byteorderimputil warnings (#2217) + * 307a7b93c Doc fix support iterators (#2219) + * 870840356 Fix typo bdldfp_decimalconverutil => bdldfp_decimalconvertutil (#2186) + * 63ddb4d93 'nullptr' test driver support for MSVC 17. (#2199) + * f7b17dd71 improve performance (#2216) + * 0e8fe7c9b Drqs 132645627/bdlcc singleproducerqueue simplify (#2215) + * 591e25e6e bsl: Elide librt on macOS when building with CMake (#2214) * 0cc7106d3 Merge pull request #2213 from jberne4/balxml_xsd_fixup * 22ea155bd Merge pull request #2203 from fbirbacher/implicit-default-ctor-bdld-manageddatum * a9edaad73 Merge pull request #2205 from osubbotin/review/doxygen-fixes @@ -979,6 +1638,8 @@ bsl-internal (3.18.0.0) git; urgency=low * daa808c47 Merge pull request #2189 from hrosen4/inteldfp-long-size_drqs-133556280 * c62d8a27d Merge pull request #2178 from ameredit/cpp17_for_msvc_part_1 * 57c1533b1 Merge pull request #2191 from bchapman/ball_logthrottle.t.cpp-nightly-build-TC-3-drqs-133554078 + * a27dacec8 bdlcc_cache: add move semantics (#2053) + * 9b0c8d280 Drqs 132301198/bdlcc singleconsumerqueue (#2181) * e726d352c Merge pull request #2188 from hversche/doc-nits * 68c81ce3c Merge pull request #2155 from bchapman/bslstl_stringref-faster-compares-drqs-128912256 * 521651b44 Merge pull request #2187 from akao30/fix/log-contract @@ -999,6 +1660,7 @@ bsl-internal (3.18.0.0) git; urgency=low * 17ee205fa Merge pull request #2136 from jberne4/drqs-130748829-bdl-trivial_asserts_bdl * 8a44a9a48 Merge pull request #2137 from jberne4/drqs-130748829-bal-trivial_asserts_bal * 8371218b1 Merge pull request #2172 from jberne4/assert_extern_filename_hack_2 + * 1da8a9f40 Drqs 132623899/bdlcc singleproducerqueue performance (#2173) * 523c07aae Merge pull request #2170 from hrosen4/sun-string-hash_dqrs-132030795 * c4eded903 Merge pull request #2147 from dsokolov7/review/bde-decimal-failures-drqs-127803631 * ee4937475 Merge pull request #2167 from dsokolov7/review/bde-formatter-failure-drqs-131079298 @@ -1050,6 +1712,7 @@ bsl-internal (3.15.0.0) git; urgency=low * b89851c49 Merge pull request #2110 from bchapman/bsls_stackaddressutil-TC-7-nightly-build-drqs-130211744 * 4eb392614 Merge pull request #2122 from spackard/master * 8f30d1c35 Merge pull request #2126 from osubbotin/review/pcre2-version-update + * 77d2a61d0 Update pcre2 README for 10.32 (#2125) * 3e9dae347 Merge pull request #2123 from osubbotin/review/pcre2-version-update * 652f286d4 Merge pull request #2117 from spackard/master * 1c74fbcca Merge pull request #2118 from jmendelsohn4/bdlcc_singleproducerqueueimple-remove-TBD-for-padding @@ -1067,6 +1730,7 @@ bsl-internal (3.15.0.0) git; urgency=low * 9ffb9aade Merge pull request #2107 from jmendelsohn4/nb20181002/bdlcc_fixedqueue-fix-usage-example-race * 49aed9606 Merge pull request #2042 from osubbotin/review/usage-ballst_stacktraceutil * 36c5320ca Merge pull request #2106 from jmendelsohn4/nb20181002/bdlcc_singleproducerqueue-nb-fixes + * 5cb79705b Prototype/bdlcc singleproducerunboundedqueue 20180816 (#2082) * d307f541f Merge pull request #2104 from oimanuel/hashAppend_bdlb_bigendian-drqs-128723942 * 6cdf073bd Merge pull request #2099 from oimanuel/fixdoc_bdlb_variant-drqs-123553441 @@ -1172,6 +1836,7 @@ bsl-internal (3.11.0.0) git; urgency=low * 8c6907a9d Merge pull request #1943 from cwilson/bslmf_decay-macro-clash-with-ISFUNC-drqs-123483201 * 37f2eda5a Merge pull request #1940 from bchapman/ball_recordattributes-avoid-null-stringref.data-drqs-123123158 * 30433310b Merge pull request #1935 from osubbotin/review/ball_error_buffer + * a7c8068d1 Add a description file for bdlpcre. (#1941) * 3885dfae9 Merge pull request #1928 from afeher/drqs-123047215-blob-move-and-swap * 99746b6b0 Merge pull request #1918 from hversche/rule-based-logging-doc-drqs-122796660 * 887d5b23a Merge pull request #1930 from afeher/drqs-123333827-bce-to-bslmt-in-docu @@ -1194,6 +1859,7 @@ bsl-internal (3.11.0.0) git; urgency=low * 01272b5ee Merge pull request #1899 from afeher/drqs-122349536-ref-to-temp * a983444c8 Merge pull request #1898 from gjenkins/unselected_choice_balber_options_test2 * 521d0e6b4 Merge pull request #1903 from osubbotin/review/balber-encoder-fix + * 483b6bc6f Fixed trivial spelling error and wrapped line as per doc standards (#1896) * ddeec5464 Merge pull request #1894 from gjenkins/unselected_choice_balber_options_test * 02ff4e582 Merge pull request #1868 from bchapman/kill-guards-bde-repo-no-bsl-no-btl-drqs-121473090 * ae4b29aec Merge pull request #1818 from gjenkins/unselected_choice_balber @@ -1237,6 +1903,8 @@ bsl-internal (3.11.0.0) git; urgency=low * 4251a3a95 Merge pull request #1787 from hrosen4/byteorder-ntohl_drqs-118956214 * 400c177fd Merge pull request #1795 from cwilson/remove-workarounds-for-drqs-117439200-from-userfieldvalue-test-driver * cef160b28 Merge pull request #1793 from cwilson/post-bde-3.5-ball-enhancements-baseline-v3.0 + * 9b22fa2fe Broadcast observer and new LoggerManager API. (#1047) + * 60c373782 Class scope category logging (DRQS 92250198) (#1060) * 0923ab50d Merge pull request #1791 from ameredit/drqs119246745_fix_gcc_warning_in_forwardutil * ae21bb96a Merge pull request #1694 from cwilson/lwg-defect-2268-default-npos-arg-for-string-methods-RDSIBDE-1792 * 17a4ee62c Merge pull request #1786 from afeher/drqs-118964602-advancetoendnoderaw-fails-on-attributes @@ -1253,6 +1921,7 @@ bsl-internal (3.11.0.0) git; urgency=low * 145da99d9 Merge pull request #1739 from fbirbacher/drqs-117090632/datummaker-no-perfect-forwarding * 7bfd9d805 Merge pull request #1770 from hrosen4/ub-left-shift_drqs-114474016 * 5cbb7bc15 Merge pull request #1719 from dstone50/bdet_TimeInterval-ISO-8601-drqs-61756773 + * d97c0794e Cmake build layers (#1754) * 148ea97aa Merge pull request #1757 from hrosen4/bad-conversion_drqs-118092561 * df0483028 Merge pull request #1695 from rbhindwa/baljsn_formatter-create-component-drqs-40644741 * b507b6fb0 Merge pull request #1753 from hrosen4/foolish-test_drqs-105506799 @@ -1508,6 +2177,7 @@ bsl-internal (3.5.0.0) git; urgency=low * d396e415c drqs-111478499: 'bdlcc_objectcatalog.h': Recode for 'bdedox' conversion. * 3236a9b6c Merge pull request #1545 from cwilson/bsls_objectbuffer-doc-typos-drqs-112139590 * 48a37084c Merge pull request #1523 from bchapman/bsls_bslonce-BSLS_BSLONCE_INITIALIZER-drqs-111273324 + * 811e7781e Typo in comments has been fixed. (#1539) * 5ee1e409f Merge pull request #1517 from afeher/drqs-111658129-fix-negative-nan-on-linux * ecec2e090 Merge pull request #1530 from sbreitst/bdedox_fixes-drqs-111740206.master * 76801745c drqs-111740206: master: Format fixes for 'bdedox' conversion. @@ -1646,6 +2316,7 @@ bsl-internal (3.5.0.0) git; urgency=low * afbf4b7a7 Merge pull request #1427 from osubbotin/review/btlso_resolvutil_warnings-drqs-109388019 * 8b1c5f0c2 Merge pull request #1414 from oimanuel/fix_bslmt_threadutil_t_14-drqs-106536608 * 9d31a06df Merge pull request #1415 from ameredit/assume_vc_2010 + * bd4b3ed18 Resolve local address. DRQS 102617223 (#1205) * a836f9292 Merge pull request #1412 from bde/revert-1409-revert-1371-drqs-107705012-int-map-builder * 31421e549 Merge pull request #1410 from afeher/drqs-1007705012-fix-master * 47eb8a7fb Merge pull request #1409 from afeher/revert-1371-drqs-107705012-int-map-builder @@ -1818,6 +2489,7 @@ bsl-internal (3.5.0.0) git; urgency=low * d50845af2 Conversion warning * c11b22604 Merge pull request #1130 from rbhindwa/btlb_blob-zero-sized-buffers-disable-asserts-drqs-97690451 * 25fbff79e Merge pull request #1103 from cwilson/bdlb_variant-add-more-remove_const + * 2b494321c Adopt commit d5f7d50d8 from bde-upstream-master: Add deprecation for loadLocalTimeOffset to .cpp files. (#1137) * 9d326c7f2 Adopt commit f3a755728 from master: Merge pull request #1109 from hrosen4/resize-empty-vector_drqs-99966534 * f22902a5f Adopt commit 476e00362 from master: Merge pull request #1104 from hrosen4/vector-string-resize-drqs-99966534 * 005569e1f Adopt commit 52af0c45b from master: Merge pull request #1093 from cwilson/remove-cycle-from-bslalg-drqs-99635010 @@ -1855,6 +2527,7 @@ bsl-internal (3.5.0.0) git; urgency=low * d0048f3d7 Adopt commit c0317f790 from master: Merge pull request #1069 from ameredit/cc_12_4_no_default_template_args * c6bea5415 Adopt commit 4c89b8e54 from master: Merge pull request #1056 from abeels/bdlcc_objectcatalog-use-objectbuffer-instead-of-manual-alignment * b96dabb7c Adopt commit 43e3718cb from master: Merge pull request #1074 from bchapman/balst-elf-xcoff-gt-4G-execs-drqs-98571127 + * d5f7d50d8 Add deprecation for loadLocalTimeOffset to .cpp files. (#1137) * 793c1d30a fixed version number * f3a755728 Merge pull request #1109 from hrosen4/resize-empty-vector_drqs-99966534 * 476e00362 Merge pull request #1104 from hrosen4/vector-string-resize-drqs-99966534 @@ -1875,6 +2548,7 @@ bsl-internal (3.5.0.0) git; urgency=low * f1e06f255 Merge pull request #1086 from osubbotin/review/strict-std-allocator-support-in-map-drqs-XXXXXXXX * 283ed4bf4 Merge pull request #1030 from rbhindwa/btlmt-connect-listen-options-drqs-76893072-rebased * 31dca8dd0 Merge pull request #1076 from hrosen4/balst_stacktraceresolverimpl_elf-suncc-5.14 + * 55f427acd Move Datum::DataType::k_NUM_TYPES out of the enum [DRQS 98512307]. (#1094) * bc921c906 Merge pull request #1095 from jmendelsohn4/drqs-99727887/sun_nb_20170424 * 576d0e4ff Merge pull request #1087 from glaptev1/glaptev1/baltzo_timezoneutil_test-drqs-98925106 * 492e0c980 Merge pull request #1089 from jmendelsohn4/drqs-99630786/aix_nb_20170421 @@ -1990,6 +2664,8 @@ bsl-internal (3.5.0.0) git; urgency=low * d9f7d38a7 Merge pull request #1020 from hversche/revert-const-date-accessor-drqs-93897974 * b701864a9 Merge pull request #1019 from oimanuel/bslmt_qlock_typo-drqs-97246034 * 9fc80f99d Merge pull request #1017 from hrosen4/poll-btlso_inetstreamsocket-drqs-97192308 + * 156674c94 'size' accessor has been added to 'bslstl::StringRef'. (#1014) + * 6254f7a03 add 'bsltf'-specific 'operator<<' overloads that were missing (#1012) * e5161616f Merge pull request #998 from cwilson/bslstl-queue-stack-broken-doxygen-links-drqs-96691374 * 442cfc182 Merge pull request #995 from mgiroux/pr/994-rebased-master * 5f112c1d6 Merge pull request #1000 from cwilson/sessionpool-usage-side-effects-within-assert-drqs-31777798 @@ -2077,6 +2753,7 @@ bsl-internal (3.5.0.0) git; urgency=low * 96a10a1ee Merge pull request #954 from dsokolov7/review/copy_n-replacement-drqs-93354460 * 48f48e75f Merge pull request #962 from mgiroux/review/rename-BUF_SIZE-to-BAEJSN_BUF_SIZE-master-drqs-95448891 * e27feeebc Merge pull request #956 from jmendelsohn4/drqs-95303426/day_count_conventions_contract_violation_20170119 + * 701fd1fee bslstl_unorderedmap: remove 'explicit' on initializer_list c'tor (#939) * 18e2f3973 Merge pull request #924 from oimanuel/bdlmt_threadpool-assert-drqs-94116649 * c53916559 Merge pull request #952 from bde/ajm_updates_pablos_function_patch * be1884606 Merge pull request #951 from hrosen4/allocator-bdlmt_multiqueuethreadpool-drqs-95127390 @@ -2092,6 +2769,7 @@ bsl-internal (3.5.0.0) git; urgency=low * b51224498 Merge pull request #849 from osubbotin/review/reference_in_map-drqs83862770 * 53ae9207f Merge pull request #917 from ameredit/remove_cpp11_support_in_gcc47 * b441b1224 Merge pull request #930 from cwilson/ball-clean-up-following-oleg-clean-up + * 679d1ed6e Cleanup of ball components. No functional changes. (#927) * 89bf39e78 Merge pull request #922 from abeels/bslalg_scalarprimitives-copyconstruct-failure * 5209f9590 Merge pull request #921 from abeels/memberfunctionpointer-adl-failure * 199807742 Merge pull request #923 from osubbotin/review/fix-the-the-drqs-none @@ -2125,11 +2803,14 @@ bsl-internal (3.5.0.0) git; urgency=low * 0fc97070c Merge pull request #896 from bchapman/bslstl_string-operator=-from-stringrefdata-drqs-57309245 * 4a377996c Merge pull request #906 from osubbotin/review/bsls-timeutils-return-asserts-drqs-93974802 * 93a08b3fb Merge pull request #907 from hversche/pr/584-rebases_and_squash + * 02b880906 [RDSIBDE-1405] Apply tests for standard allocators to deque (#863) * 2d829ef3a Merge pull request #873 from hrosen4/aix-poll-defs-drqs-92487083 * 1d5b03494 Merge pull request #904 from spackard/master + * f1f896d5d Fix compilation error on Darwin. (#903) * c3087cf57 Merge pull request #880 from che2/move-stackaddressutil-bsls-drqs-91800628 * e19ebf1c2 Merge pull request #899 from osubbotin/review/bdlb-tokenizer-usage-drqs-93783862 * e5bdade3e Merge pull request #898 from abeels/fix-bdld_datummaker-cpp03-build + * 5da2b6dee Add tests for standard allocators support for bslstl_list (#886) * 24f31079b Merge pull request #900 from cwilson/use-bslmf_resulttype-in-bdlf_bind-drqs-93234786 * 4152cba8d Adopt commit 9a9a1be from master: Merge pull request #894 from cwilson/propagate-map-free-operator-doc-across-bslstl-drqs-93639116 * 4d472f878 Adopt commit 96bbb3e from master: Merge pull request #895 from cwilson/remove-AT-REVIEW_FOR_MASTER-from-bsl-header-files @@ -2229,6 +2910,7 @@ bsl-internal (3.5.0.0) git; urgency=low * 47fb00c3c Merge pull request #856 from jmendelsohn4/drqs-92491241/bdlma_sequentialpool_reserveCapacity * 42a2f2f8f Merge pull request #844 from dgoffred/cpp11-algos-fixed * 99b5fff4e Merge pull request #847 from dschuman/review/dschumann-drqs-69660838 + * 45ecf3e7d Tests for standard allocators have been applied for 'bsl::unordered_set'. (#793) * 4a4004ea8 Merge pull request #853 from che2/fix-balxml_formatter-test-failure * 45f26ad9b Merge pull request #852 from hversche/pr/729-rebased-master * 501105e92 Merge pull request #698 from hversche/bsls_log_performance @@ -2250,6 +2932,7 @@ bsl-internal (3.5.0.0) git; urgency=low * 614bfbef1 Merge pull request #831 from ameredit/late_windows_fix_for_isenum * 7404a9363 Merge pull request #825 from ameredit/fix_string_test_driver_for_cpp11 * a0767cf68 Merge pull request #815 from ameredit/fix_hash_for_msvc2015 + * a4f73046a Tests for standard allocators have been applied for 'bsl::set'. RDSIBDE-1430 (#774) * a11afd287 Merge pull request #826 from ameredit/incomplete_keys_in_unordered_maps * 86bbe7c50 Merge pull request #797 from cwilson/bdlb_nullablevalue-fix-cpp11-robo-build-breakage * 66d508199 Merge pull request #824 from ameredit/noexcept_for_utilities @@ -2295,8 +2978,12 @@ bsl-internal (3.5.0.0) git; urgency=low * cc9529152 Merge pull request #812 from bchapman/bsltf-fix-stateful-and-value-array-drqs-91330187 * 447f57515 Merge pull request #700 from phalpern/function-no-partial * 5205d60a0 Merge pull request #549 from phalpern/bslmf_memberfunctionpointertraits + * 03161a6c0 Fix test driver issues from PR 787 (#807) * 8da84cf4d Merge pull request #782 from jmendelsohn4/drqs-90511308/bdlde_quotedprintablencoder_fix_conversion_add_test_driver + * 297565d3f Test driver and documentation changes allocemplacabletesttype (#791) + * 7efd2fbb2 Hot fix: excessive template instantiation in 'bsltf_stdstatefulallocator' (#810) * d0648de24 Merge pull request #805 from glaptev1/feature/add-bsl-difinition-for-memory_order-enumeration-values-drqs-89923897 + * d92eb996f Improvement of test driver for 'bsltf_stdstatefulallocator'. DRQS 88258671. (#732) * 29e2f8fce Merge pull request #800 from abeels/bsls_assert-log-handler-return-jira-1460 * c7abbe09d Merge pull request #801 from jmendelsohn4/drqs-90203436/master/bdlt_datetimeinterval_instrument_range * a9aba8e86 Merge pull request #787 from cwilson/test-allocator-propagation-traits-across-bslstl @@ -2305,6 +2992,7 @@ bsl-internal (3.5.0.0) git; urgency=low * 67916d351 Merge pull request #794 from che2/fix-berutil-drqs-89283849 * a48edb317 Merge pull request #785 from dschuman/review/dschumann-drqs-89789150-mqtp * 25735fe1a Merge pull request #790 from jmendelsohn4/nb/bdlde_crc64_20161013 + * ab56cb1aa Improvement of test driver for 'bsltf_stdallocatoradaptor'. DRQS 8619930. (#616) * 0aeee8afe Merge pull request #788 from ameredit/unbreak_the_build * 585015ce0 Merge pull request #780 from sbreitst/noexcept_test_cases * c1226f11d Merge pull request #786 from mgiroux/review/make-threadutil-imp-public diff --git a/debian/cmakedpkgconfig b/debian/cmakedpkgconfig index 09cf9ec59b..4de4886c5d 100644 --- a/debian/cmakedpkgconfig +++ b/debian/cmakedpkgconfig @@ -25,7 +25,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-headers", "pcre2-meta", "pcre2-release-symlink", - "pcre2-pkgconfig" ] + "pcre2-pkgconfig" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-headers", "ryu-meta", + "ryu-release-symlink", + "ryu-pkgconfig" ] } }, { @@ -51,7 +55,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-release-symlink", "pcre2-pkgconfig", - "pcre2-pic-symlink-hack" ] + "pcre2-pic-symlink-hack" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-release-symlink", + "ryu-pkgconfig", + "ryu-pic-symlink-hack" ] } }, { @@ -87,7 +95,8 @@ "libbdl-dev-extra": [ "bdl", "bdl-symlinks" ], "libbsl-dev-extra": [ "bsl", "bsl-symlinks" ], "libinteldfp-dev-extra": [ "inteldfp", "inteldfp-symlinks" ], - "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ] + "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ], + "libryu-dev-extra": [ "ryu", "ryu-symlinks" ] } }, { @@ -129,7 +138,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-headers", "pcre2-meta", "pcre2-release-symlink", - "pcre2-pkgconfig" ] + "pcre2-pkgconfig" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-headers", "ryu-meta", + "ryu-release-symlink", + "ryu-pkgconfig" ] } }, { @@ -155,7 +168,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-release-symlink", "pcre2-pkgconfig", - "pcre2-pic-symlink-hack" ] + "pcre2-pic-symlink-hack" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-release-symlink", + "ryu-pkgconfig", + "ryu-pic-symlink-hack" ] } }, { @@ -191,7 +208,8 @@ "libbdl-dev-extra": [ "bdl", "bdl-symlinks" ], "libbsl-dev-extra": [ "bsl", "bsl-symlinks" ], "libinteldfp-dev-extra": [ "inteldfp", "inteldfp-symlinks" ], - "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ] + "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ], + "libryu-dev-extra": [ "ryu", "ryu-symlinks" ] } }, { @@ -233,7 +251,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-headers", "pcre2-meta", "pcre2-release-symlink", - "pcre2-pkgconfig" ] + "pcre2-pkgconfig" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-headers", "ryu-meta", + "ryu-release-symlink", + "ryu-pkgconfig" ] } }, { @@ -259,7 +281,11 @@ "libpcre2-dev": [ "pcre2", "pcre2-symlinks", "pcre2-release-symlink", "pcre2-pkgconfig", - "pcre2-pic-symlink-hack" ] + "pcre2-pic-symlink-hack" ], + "libryu-dev": [ "ryu", "ryu-symlinks", + "ryu-release-symlink", + "ryu-pkgconfig", + "ryu-pic-symlink-hack" ] } }, { @@ -295,7 +321,8 @@ "libbdl-dev-extra": [ "bdl", "bdl-symlinks" ], "libbsl-dev-extra": [ "bsl", "bsl-symlinks" ], "libinteldfp-dev-extra": [ "inteldfp", "inteldfp-symlinks" ], - "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ] + "libpcre2-dev-extra": [ "pcre2", "pcre2-symlinks" ], + "libryu-dev-extra": [ "ryu", "ryu-symlinks" ] } }, { diff --git a/debian/control b/debian/control index 87a2e71085..4b0640794b 100644 --- a/debian/control +++ b/debian/control @@ -37,11 +37,11 @@ Description: BDL groups/bdl extra libraries Package: libbsl-dev-extra -Depends: libbsl-dev +Depends: libbsl-dev, libryu-dev-extra Architecture: any Conflicts: libbsl-dev (<< 3.41.0.0), libbsl-extra-dev Replaces: libbsl-extra-dev -Description: BSL +Description: BSL groups/bsl extra libraries @@ -65,8 +65,9 @@ Description: BDL groups/bdl development files Package: libbsl-dev +Depends: libryu-dev Architecture: any -Description: BSL +Description: BSL groups/bsl development files @@ -74,7 +75,7 @@ Description: BSL Package: libinteldfp-dev-extra Depends: libinteldfp-dev Architecture: any -Description: Intel Decimal Floating-Point Math Library +Description: Intel Decimal Floating-Point Math Library thirdparty/inteldfp extra libraries Package: libinteldfp-dev @@ -92,3 +93,15 @@ Description: PCRE 2 Regular Expression Library Package: libpcre2-dev Architecture: any Description: PCRE 2 Regular Expression Library + + + +Package: libryu-dev-extra +Depends: libryu-dev +Architecture: any +Description: Bloomberg LP Extended Ryu Float Formatting Library + thirdparty/ryu extra libraries + +Package: libryu-dev +Architecture: any +Description: Bloomberg LP Extended Ryu Float Formatting Library diff --git a/debian/libryu-dev-extra.lintian-overrides b/debian/libryu-dev-extra.lintian-overrides new file mode 100644 index 0000000000..25d9a17088 --- /dev/null +++ b/debian/libryu-dev-extra.lintian-overrides @@ -0,0 +1 @@ +libryu-dev-extra: blp-bde-metadata-missing diff --git a/debian/rules b/debian/rules index 1a7b217cfe..b27b9a4df4 100644 --- a/debian/rules +++ b/debian/rules @@ -5,7 +5,7 @@ include $(DEBHELPER_PATH)/bde-debhelper.cmake2.mk debian/bde-build-stamp: debian/lintian-overrides-stamp debian/lintian-overrides-stamp: - for uor in bal bbl bdl bsl inteldfp pcre2; do \ + for uor in bal bbl bdl bsl inteldfp pcre2 ryu; do \ mkdir -p debian/lib$${uor}-dev-extra$(PREFIX)/share/lintian/overrides; \ cp debian/lib$${uor}-dev-extra.lintian-overrides \ debian/lib$${uor}-dev-extra$(PREFIX)/share/lintian/overrides/lib$${uor}-dev-extra; \ diff --git a/groups/bal/balb/balb_controlmanager.cpp b/groups/bal/balb/balb_controlmanager.cpp index 7d3d7f72cf..0965714db2 100644 --- a/groups/bal/balb/balb_controlmanager.cpp +++ b/groups/bal/balb/balb_controlmanager.cpp @@ -12,7 +12,10 @@ #include #include +#include + #include +#include #include #include @@ -26,32 +29,20 @@ #include namespace BloombergLP { +namespace balb { -namespace { - -template -struct MapKeyExtractor { - const FIRST& operator() (const bsl::pair& pair) { - return pair.first; - } -}; - -template -MapKeyExtractor -GetExtractor(const bsl::map&) { - return MapKeyExtractor(); -} + // -------------------------------------- + // class ControlManager::CaselessLessThan + // -------------------------------------- inline -bool isLessThanCaseless(const bsl::string& lhsString, - const bsl::string& rhsString) +bool ControlManager::CaselessLessThan::operator()( + const bsl::string_view& lhs, + const bsl::string_view& rhs) const { - return -1 == bdlb::String::lowerCaseCmp(lhsString, rhsString); + return bdlb::StringViewUtil::lowerCaseCmp(lhs, rhs) < 0; } -} // close unnamed namespace - -namespace balb { // -------------------- // class ControlManager // -------------------- @@ -60,7 +51,7 @@ namespace balb { ControlManager::ControlManager(bslma::Allocator *basicAllocator) : d_allocator_p(bslma::Default::allocator(basicAllocator)) -, d_registry(&isLessThanCaseless, basicAllocator) +, d_registry(basicAllocator) { } ControlManager::~ControlManager() @@ -68,42 +59,54 @@ ControlManager::~ControlManager() // MANIPULATORS -int ControlManager::registerHandler(const bsl::string& prefix, - const bsl::string& arguments, - const bsl::string& description, - const ControlHandler& handler) +int ControlManager::registerHandler(const bsl::string_view& prefix, + const bsl::string_view& arguments, + const bsl::string_view& description, + const ControlHandler& handler) { + typedef Registry::iterator Iterator; + bslmt::WriteLockGuard guard(&d_registryMutex); int rc = 0; ControlManager_Entry entry(handler, arguments, description); - Registry::iterator it = d_registry.find(prefix); + Iterator it = d_registry.find(prefix); if (it != d_registry.end()) { it->second = entry; rc = 1; } else { - d_registry.insert(bsl::make_pair(prefix, entry)); + bsl::pair rcPair = d_registry.emplace(prefix, entry); + BSLS_ASSERT(rcPair.second); (void) rcPair; } return rc; } -int ControlManager::deregisterHandler(const bsl::string& prefix) +int ControlManager::deregisterHandler(const bsl::string_view& prefix) { bslmt::WriteLockGuard guard(&d_registryMutex); - return (0 == d_registry.erase(prefix)); + Registry::iterator it = d_registry.find(prefix); + if (d_registry.end() != it) { + d_registry.erase(it); + + return 0; // RETURN + } + + return 1; } -int ControlManager::dispatchMessage(const bsl::string& message) const +int ControlManager::dispatchMessage(const bsl::string_view& message) const { - BSLS_LOG_TRACE("Dispatching control message '%s'", message.c_str()); + const int len = static_cast(message.length()); + BSLS_LOG_TRACE("Dispatching control message '%.*s'", len, message.data()); - bsl::string token; - bsl::istringstream messageStream(message); + bdlsb::FixedMemInStreamBuf isb(message.data(), message.length()); + bsl::istream messageStream(&isb); + bsl::string token; messageStream >> token; bslmt::ReadLockGuard registryGuard(&d_registryMutex); @@ -122,6 +125,9 @@ int ControlManager::dispatchMessage(const bsl::string& message) const int ControlManager::dispatchMessage(const bsl::string& prefix, bsl::istream& stream) const { + // Imp note: 'prefix' has to be 'bsl::string' and not a string view for the + // sake of being able to be passed to the callback. + BSLS_LOG_TRACE("Dispatching control message '%s'", prefix.c_str()); bslmt::ReadLockGuard registryGuard(&d_registryMutex); @@ -137,8 +143,8 @@ int ControlManager::dispatchMessage(const bsl::string& prefix, return -1; } -void ControlManager::printUsage(bsl::ostream& stream, - const bsl::string& preamble) const +void ControlManager::printUsage(bsl::ostream& stream, + const bsl::string_view& preamble) const { stream << preamble << bsl::endl; d_registryMutex.lockRead(); @@ -174,8 +180,8 @@ ControlManager::ControlManager_Entry::~ControlManager_Entry() ControlManager::ControlManager_Entry::ControlManager_Entry( const ControlManager::ControlHandler& callback, - const bsl::string& arguments, - const bsl::string& description, + const bsl::string_view& arguments, + const bsl::string_view& description, bslma::Allocator *basicAllocator) : d_callback(bsl::allocator_arg_t(), bsl::allocator(basicAllocator), diff --git a/groups/bal/balb/balb_controlmanager.h b/groups/bal/balb/balb_controlmanager.h index 68cc81960b..84faa3b638 100644 --- a/groups/bal/balb/balb_controlmanager.h +++ b/groups/bal/balb/balb_controlmanager.h @@ -147,8 +147,8 @@ class ControlManager_Entry { ControlManager_Entry( const ControlManager::ControlHandler& callback, - const bsl::string& arguments, - const bsl::string& description, + const bsl::string_view& arguments, + const bsl::string_view& description, bslma::Allocator *basicAllocator = 0); // Create an ControlManager_Entry object with the specified initial // values. @@ -193,9 +193,20 @@ class ControlManager_Entry { // object. }; + struct CaselessLessThan { + // TYPES + typedef void is_transparent; + + // ACCESSOR + bool operator()(const bsl::string_view& lhs, + const bsl::string_view& rhs) const; + // Return 'true' if the specified 'lhs' is less than the specified + // 'rhs' in a case-insensitive comparison, and 'false' otherwise. + }; + typedef bsl::map Registry; + CaselessLessThan> Registry; // Defines a type alias for the ordered associative data structure // that maps a message prefix to a 'StringComparator' functor. @@ -223,10 +234,10 @@ class ControlManager_Entry { // Destroy this object. // MANIPULATORS - int registerHandler(const bsl::string& prefix, - const bsl::string& arguments, - const bsl::string& description, - const ControlHandler& handler); + int registerHandler(const bsl::string_view& prefix, + const bsl::string_view& arguments, + const bsl::string_view& description, + const ControlHandler& handler); // Register the specified 'handler' to be invoked whenever a control // message having the specified case-insensitive 'prefix' is received // for this control manager. Also register the specified 'arguments' @@ -236,13 +247,13 @@ class ControlManager_Entry { // callback was replaced, return 0 if no replacement occurred, and // return a negative value otherwise. - int deregisterHandler(const bsl::string& prefix); + int deregisterHandler(const bsl::string_view& prefix); // Deregister the callback function previously registered to handle the // specified 'prefix'. Return 0 on success or a non-zero value // otherwise. // ACCESSOR - int dispatchMessage(const bsl::string& message) const; + int dispatchMessage(const bsl::string_view& message) const; // Parse the specified complete 'message' and dispatch it. Return // 0 on success, and a non-zero value otherwise; in particular return // non-zero if no registered callback could be found for the @@ -255,14 +266,15 @@ class ControlManager_Entry { // non-zero if no registered callback could be found for the // case-insensitive 'prefix'. - void printUsage(bsl::ostream& stream, const bsl::string& preamble) const; + void printUsage(bsl::ostream& stream, + const bsl::string_view& preamble) const; // Print to the specified 'stream' the specified 'preamble' text, // followed by the registered commands and documentation for this // control manager. Note that a newline is appended to 'preamble' in // the output. - void printUsageHelper(bsl::ostream *stream, - const bsl::string& preamble) const; + void printUsageHelper(bsl::ostream *stream, + const bsl::string_view& preamble) const; // Invoke 'printUsage' passing the specified '*stream' and 'preamble'. // Suitable for binding using the bdlf::BindUtil package. @@ -322,8 +334,8 @@ const bsl::string& ControlManager::ControlManager_Entry::description() const // ACCESSORS inline -void ControlManager::printUsageHelper(bsl::ostream *stream, - const bsl::string& preamble) const +void ControlManager::printUsageHelper(bsl::ostream *stream, + const bsl::string_view& preamble) const { printUsage(*stream, preamble); } diff --git a/groups/bal/balb/balb_filecleanerconfiguration.h b/groups/bal/balb/balb_filecleanerconfiguration.h index 5de72887e3..aa3c6a8e67 100644 --- a/groups/bal/balb/balb_filecleanerconfiguration.h +++ b/groups/bal/balb/balb_filecleanerconfiguration.h @@ -141,7 +141,7 @@ class FileCleanerConfiguration { // supply memory. If 'basicAllocator' is 0, the currently installed // default allocator is used. - FileCleanerConfiguration(const bslstl::StringRef& filePattern, + FileCleanerConfiguration(const bsl::string_view& filePattern, const bsls::TimeInterval& maxAge, int minNumber, bslma::Allocator *basicAllocator = 0); @@ -168,7 +168,7 @@ class FileCleanerConfiguration { // of the specified 'rhs' object, and return a reference providing // modifiable access to this object. - void setFilePattern(const bslstl::StringRef& filePattern); + void setFilePattern(const bsl::string_view& filePattern); // Set the file pattern attribute of this object to the specified // 'filePattern'. @@ -246,7 +246,7 @@ FileCleanerConfiguration::FileCleanerConfiguration( inline FileCleanerConfiguration::FileCleanerConfiguration( - const bslstl::StringRef& filePattern, + const bsl::string_view& filePattern, const bsls::TimeInterval& maxAge, int minNumber, bslma::Allocator *basicAllocator) @@ -280,7 +280,7 @@ FileCleanerConfiguration::operator=(const FileCleanerConfiguration& rhs) inline void FileCleanerConfiguration::setFilePattern( - const bslstl::StringRef& filePattern) + const bsl::string_view& filePattern) { d_filePattern = filePattern; } diff --git a/groups/bal/balb/balb_filecleanerutil.t.cpp b/groups/bal/balb/balb_filecleanerutil.t.cpp index ea726f3522..eb82514e8a 100644 --- a/groups/bal/balb/balb_filecleanerutil.t.cpp +++ b/groups/bal/balb/balb_filecleanerutil.t.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -123,69 +124,6 @@ typedef balb::FileCleanerConfiguration ObjConfig; namespace { -class TempDirectoryGuard { - // This class implements a scoped temporary directory guard. The guard - // tries to create a temporary directory in the system-wide temp directory - // and falls back to the current directory. - - // DATA - bsl::string d_dirName; // path to the created directory - bslma::Allocator *d_allocator_p; // memory allocator (held, not owned) - - // NOT IMPLEMENTED - TempDirectoryGuard(const TempDirectoryGuard&); - TempDirectoryGuard& operator=(const TempDirectoryGuard&); - - public: - // TRAITS - BSLMF_NESTED_TRAIT_DECLARATION(TempDirectoryGuard, - bslma::UsesBslmaAllocator); - - // CREATORS - explicit TempDirectoryGuard(bslma::Allocator *basicAllocator = 0) - // Create temporary directory in the system-wide temp or current - // directory. Optionally specify a 'basicAllocator' used to supply - // memory. If 'basicAllocator' is 0, the currently installed default - // allocator is used. - : d_dirName(bslma::Default::allocator(basicAllocator)) - , d_allocator_p(bslma::Default::allocator(basicAllocator)) - { - bsl::string tmpPath(d_allocator_p); -#ifdef BSLS_PLATFORM_OS_WINDOWS - char tmpPathBuf[MAX_PATH]; - GetTempPath(MAX_PATH, tmpPathBuf); - tmpPath.assign(tmpPathBuf); -#else - const char *envTmpPath = bsl::getenv("TMPDIR"); - if (envTmpPath) { - tmpPath.assign(envTmpPath); - } -#endif - - int res = bdls::PathUtil::appendIfValid(&tmpPath, "ball_"); - ASSERTV(tmpPath, 0 == res); - - res = bdls::FilesystemUtil::createTemporaryDirectory(&d_dirName, - tmpPath); - ASSERTV(tmpPath, 0 == res); - } - - ~TempDirectoryGuard() - // Destroy this object and remove the temporary directory (recursively) - // created at construction. - { - bdls::FilesystemUtil::remove(d_dirName, true); - } - - // ACCESSORS - const bsl::string& getTempDirName() const - // Return a 'const' reference to the name of the created temporary - // directory. - { - return d_dirName; - } -}; - int changeModificationTime(const bsl::string& fileName, int delta) // Change the modification time of the file with the specified 'fileName' @@ -366,8 +304,8 @@ int main(int argc, char *argv[]) if (verbose) cout << "\tTesting removal on time only." << endl; { - TempDirectoryGuard tempDirGuard; - bsl::string baseName(tempDirGuard.getTempDirName()); + bdls::TempDirectoryGuard tempDirGuard("balb_"); + bsl::string baseName(tempDirGuard.getTempDirName()); bdls::PathUtil::appendRaw(&baseName, "logFile"); createFile(baseName + "1"); @@ -412,8 +350,8 @@ int main(int argc, char *argv[]) if (verbose) cout << "\tTesting removal on time and number." << endl; { - TempDirectoryGuard tempDirGuard; - bsl::string baseName(tempDirGuard.getTempDirName()); + bdls::TempDirectoryGuard tempDirGuard("balb_"); + bsl::string baseName(tempDirGuard.getTempDirName()); bdls::PathUtil::appendRaw(&baseName, "logFile"); createFile(baseName + "1"); @@ -480,8 +418,8 @@ int main(int argc, char *argv[]) cout << "\tTesting removal of files with mod time in future." << endl; { - TempDirectoryGuard tempDirGuard; - bsl::string baseName(tempDirGuard.getTempDirName()); + bdls::TempDirectoryGuard tempDirGuard("balb_"); + bsl::string baseName(tempDirGuard.getTempDirName()); bdls::PathUtil::appendRaw(&baseName, "logFile"); createFile(baseName + "1"); @@ -533,8 +471,8 @@ int main(int argc, char *argv[]) } { - TempDirectoryGuard tempDirGuard; - bsl::string baseName(tempDirGuard.getTempDirName()); + bdls::TempDirectoryGuard tempDirGuard("balb_"); + bsl::string baseName(tempDirGuard.getTempDirName()); bdls::PathUtil::appendRaw(&baseName, "logFile"); createFile(baseName + "1"); diff --git a/groups/bal/balb/balb_leakybucket.cpp b/groups/bal/balb/balb_leakybucket.cpp new file mode 100644 index 0000000000..8e2ce4cd29 --- /dev/null +++ b/groups/bal/balb/balb_leakybucket.cpp @@ -0,0 +1,335 @@ +// balb_leakybucket.cpp -*-C++-*- +#include + +#include +BSLS_IDENT_RCSID(balb_leakybucket.cpp,"$Id$ $CSID$") + +#include + +namespace BloombergLP { + +namespace { + +bsls::Types::Uint64 calculateNumberOfUnitsToDrain( + bsls::Types::Uint64* fractionalUnitDrainedInNanoUnits, + bsls::Types::Uint64 drainRate, + const bsls::TimeInterval& timeInterval) + // Return the number of units that would be drained from a leaky bucket + // over the specified 'timeInterval' at the specified 'drainRate', plus the + // specified 'fractionalUnitDrainedInNanoUnits', representing a fractional + // remainder from a previous call to 'calculateNumberOfUnitsToDrain'. Load + // into 'fractionalUnitDrainedInNanoUnits' the fractional remainder + // (between 0.0 and 1.0, represented in nano-units) from this calculation. + // The behavior is undefined unless + // '0 <= *fractionalUnitDrainedInNanoUnits < 1000000000' (i.e., it + // represents a value between 0 and 1 unit) and + // 'timeInterval.seconds() * drainRate <= ULLONG_MAX'. Note that + // 'fractionalUnitDrainedInNanoUnits' is represented in nano-units to avoid + // using a floating point representation. + +{ + const bsls::Types::Uint64 k_NANOUNITS_PER_UNIT = 1000000000; + + BSLS_ASSERT(static_cast(timeInterval.seconds()) <= + ULLONG_MAX / drainRate); + BSLS_ASSERT(0 != fractionalUnitDrainedInNanoUnits); + BSLS_ASSERT(*fractionalUnitDrainedInNanoUnits < k_NANOUNITS_PER_UNIT); + + bsls::Types::Uint64 units = drainRate * timeInterval.seconds(); + units += (drainRate / k_NANOUNITS_PER_UNIT) * timeInterval.nanoseconds(); + + bsls::Types::Uint64 nanounits = 0; + + // As long as rate is represented by a whole number, the fractional part + // of number of units to drain comes from fractional part of seconds of + // the time interval + + nanounits = *fractionalUnitDrainedInNanoUnits + + (drainRate % k_NANOUNITS_PER_UNIT) * timeInterval.nanoseconds(); + + *fractionalUnitDrainedInNanoUnits = nanounits % k_NANOUNITS_PER_UNIT; + + units += nanounits / k_NANOUNITS_PER_UNIT; + + return units; +} + +} // close unnamed namespace + +namespace balb { + //------------------ + // class LeakyBucket + //------------------ + +// CLASS METHODS +bsls::Types::Uint64 LeakyBucket::calculateCapacity( + bsls::Types::Uint64 drainRate, + const bsls::TimeInterval& timeWindow) +{ + BSLS_ASSERT(1 == drainRate || + timeWindow <= LeakyBucket::calculateDrainTime(ULLONG_MAX, + drainRate, + false)); + + bsls::Types::Uint64 fractionalUnitsInNanoUnits = 0; + + bsls::Types::Uint64 capacity = calculateNumberOfUnitsToDrain( + &fractionalUnitsInNanoUnits, + drainRate, + timeWindow); + + // Round the returned capacity to 1, which is okay, because it does not + // affect the drain rate. + + return (0 != capacity) ? capacity : 1; +} + +bsls::TimeInterval LeakyBucket::calculateDrainTime( + bsls::Types::Uint64 numUnits, + bsls::Types::Uint64 drainRate, + bool ceilFlag) +{ + BSLS_ASSERT(drainRate > 1 || numUnits <= LLONG_MAX); + + bsls::TimeInterval interval(0,0); + + interval.addSeconds(numUnits / drainRate); + bsls::Types::Uint64 remUnits = numUnits % drainRate; + + const double nanoSecs = static_cast(remUnits) * 1e9 / + static_cast(drainRate); + interval.addNanoseconds(static_cast( + ceilFlag ? ceil(nanoSecs) : floor(nanoSecs))); + + return interval; +} + +bsls::TimeInterval LeakyBucket::calculateTimeWindow( + bsls::Types::Uint64 drainRate, + bsls::Types::Uint64 capacity) +{ + + BSLS_ASSERT(drainRate > 0); + BSLS_ASSERT(drainRate > 1 || capacity <= LLONG_MAX); + + bsls::TimeInterval window = LeakyBucket::calculateDrainTime(capacity, + drainRate, + true); + + if (0 == window) { + window.addNanoseconds(1); + } + + return window; +} + +// CREATORS +LeakyBucket::LeakyBucket(bsls::Types::Uint64 drainRate, + bsls::Types::Uint64 capacity, + const bsls::TimeInterval& currentTime) +: d_drainRate(drainRate) +, d_capacity(capacity) +, d_unitsReserved(0) +, d_unitsInBucket(0) +, d_fractionalUnitDrainedInNanoUnits(0) +, d_lastUpdateTime(currentTime) +, d_statSubmittedUnits(0) +, d_statSubmittedUnitsAtLastUpdate(0) +, d_statisticsCollectionStartTime(currentTime) +{ + BSLS_ASSERT_OPT(0 < d_drainRate); + BSLS_ASSERT_OPT(0 < d_capacity); + BSLS_ASSERT(LLONG_MIN != currentTime.seconds()); + + // Calculate the maximum interval between updates that would not cause the + // number of units drained to overflow an unsigned 64-bit integral type. + + if (drainRate == 1) { + + // 'd_maxUpdateInterval' is a signed 64-bit integral type that can't + // represent 'ULLONG_MAX' number of seconds, so we set + // 'd_maxUpdateInterval' to the maximum representable value when + // 'drainRate' is 1. + + d_maxUpdateInterval = bsls::TimeInterval(LLONG_MAX, 999999999); + } + else { + d_maxUpdateInterval = LeakyBucket::calculateDrainTime(ULLONG_MAX, + drainRate, + false); + } +} + +// MANIPULATORS +bsls::TimeInterval LeakyBucket::calculateTimeToSubmit( + const bsls::TimeInterval& currentTime) +{ + bsls::Types::Uint64 usedUnits = d_unitsInBucket + d_unitsReserved; + + // Return 0-length time interval if units can be submitted right now. + + if (usedUnits < d_capacity) { + return bsls::TimeInterval(0, 0); // RETURN + } + + updateState(currentTime); + + // Return 0-length time interval if units can be submitted after the state + // has been updated. + + if (d_unitsInBucket + d_unitsReserved < d_capacity) { + return bsls::TimeInterval(0, 0); // RETURN + } + + bsls::TimeInterval timeToSubmit(0,0); + bsls::Types::Uint64 backlogUnits; + + // From here, 'd_unitsInBucket + d_unitsReserved' is always greater than + // 'd_capacity' + + backlogUnits = d_unitsInBucket + d_unitsReserved - d_capacity + 1; + + timeToSubmit = LeakyBucket::calculateDrainTime(backlogUnits, + d_drainRate, + true); + + // Return 1 nanosecond if the time interval was rounded to zero (in cases + // of high drain rates). + + if (timeToSubmit == 0) { + timeToSubmit.addNanoseconds(1); + } + + return timeToSubmit; +} + +void LeakyBucket::setRateAndCapacity(bsls::Types::Uint64 newRate, + bsls::Types::Uint64 newCapacity) +{ + BSLS_ASSERT_SAFE(0 < newRate); + BSLS_ASSERT_SAFE(0 < newCapacity); + + d_drainRate = newRate; + d_capacity = newCapacity; + + // Calculate the maximum interval between updates that would not cause the + // number of units drained to overflow an unsigned 64-bit integral type. + + + if (newRate == 1) { + d_maxUpdateInterval = bsls::TimeInterval(LLONG_MAX, 999999999); + } + else { + d_maxUpdateInterval = LeakyBucket::calculateDrainTime(ULLONG_MAX, + newRate, + false); + } +} + +void LeakyBucket::updateState(const bsls::TimeInterval& currentTime) +{ + BSLS_ASSERT(LLONG_MIN != currentTime.seconds()); + + bsls::TimeInterval delta = currentTime - d_lastUpdateTime; + d_statSubmittedUnitsAtLastUpdate = d_statSubmittedUnits; + + // If delta is greater than the time it takes to drain the maximum number + // of units representable by 64 bit integral type, then reset + // 'unitsInBucket'. + + if (delta > d_maxUpdateInterval) { + d_lastUpdateTime = currentTime; + d_unitsInBucket = 0; + d_fractionalUnitDrainedInNanoUnits = 0; + return; // RETURN + } + + if (delta >= bsls::TimeInterval(0, 0)) { + bsls::Types::Uint64 units; + units = calculateNumberOfUnitsToDrain( + &d_fractionalUnitDrainedInNanoUnits, + d_drainRate, + delta); + + if (units < d_unitsInBucket) { + d_unitsInBucket -= units; + } + else { + d_unitsInBucket = 0; + } + } + else { + // The delta maybe negative when the system clocks are updated. If the + // specified 'currentTime' precedes 'statisticsCollectionStartTime', + // adjust it to prevent the statistics collection interval from going + // negative. + + if (currentTime < d_statisticsCollectionStartTime) { + d_statisticsCollectionStartTime = currentTime; + } + } + + d_lastUpdateTime = currentTime; +} + +bool LeakyBucket::wouldOverflow(const bsls::TimeInterval& currentTime) +{ + updateState(currentTime); + + if (1 > ULLONG_MAX - d_unitsInBucket - d_unitsReserved || + d_unitsInBucket + d_unitsReserved + 1 > d_capacity) { + + return true; // RETURN + } + return false; +} + +// ACCESSORS +void LeakyBucket::getStatistics(bsls::Types::Uint64* submittedUnits, + bsls::Types::Uint64* unusedUnits) const +{ + + BSLS_ASSERT(0 != submittedUnits); + BSLS_ASSERT(0 != unusedUnits); + + *submittedUnits = d_statSubmittedUnitsAtLastUpdate; + bsls::Types::Uint64 fractionalUnits = 0; + + // 'monitoredInterval' can not be negative, as the 'updateState' method + // ensures that 'd_lastUpdateTime' always precedes + // 'statisticsCollectionStartTime'. + + bsls::TimeInterval monitoredInterval = d_lastUpdateTime - + d_statisticsCollectionStartTime; + + bsls::Types::Uint64 drainedUnits = calculateNumberOfUnitsToDrain( + &fractionalUnits, + d_drainRate, + monitoredInterval); + + if (drainedUnits < d_statSubmittedUnitsAtLastUpdate) { + *unusedUnits = 0; + } + else { + *unusedUnits = drainedUnits - d_statSubmittedUnitsAtLastUpdate; + } +} + +} // close package namespace +} // close enterprise namespace + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_leakybucket.h b/groups/bal/balb/balb_leakybucket.h new file mode 100644 index 0000000000..5af755910b --- /dev/null +++ b/groups/bal/balb/balb_leakybucket.h @@ -0,0 +1,716 @@ +// balb_leakybucket.h -*-C++-*- +#ifndef INCLUDED_BALB_LEAKYBUCKET +#define INCLUDED_BALB_LEAKYBUCKET + +#include +BSLS_IDENT("$Id: $") + +//@PURPOSE: Provide a mechanism to monitor the consumption rate of a resource. +// +//@CLASSES: +// balb::LeakyBucket: a leaky bucket rate monitor +// +//@SEE_ALSO: balb_ratelimiter +// +//@DESCRIPTION This component provides a mechanism, 'balb::LeakyBucket', that +// implements a leaky bucket algorithm that allows clients to monitor whether a +// resource is being consumed at a particular rate. +// +// The name of this mechanism, leaky bucket, derives from an analogy of pouring +// water into a bucket with a hole at the bottom. The maximum rate at which +// water will drain out the bucket depends on the size of the hole, and not on +// the rate at which water is poured into the bucket. If more water is being +// poured into the bucket than being drained, the bucket will eventually +// overflow. If the person pouring water into a leaky bucket ensures the +// bucket doesn't overflow, then the average rate they pour water will, over +// time, be limited by the rate at which water flows out of the bucket. By +// analogy, a leaky bucket provides a means to limit the rate of consumption of +// some resource (water poured into the bucket) to a configured rate (the size +// of the hole in the bucket). +// +// The behavior of a leaky bucket is determined by two properties: the capacity +// and the drain rate. The drain rate, measured in 'units/s', is the rate at +// which the resource is drained. The capacity, measured in 'units', is the +// maximum amount of the resource that the leaky bucket can hold before it +// overflows. 'unit' is a generic unit of measurement (e.g., bytes, number of +// messages, packets, liters, clock cycles, etc.). Note that the drain rate +// determines average rate of resource consumption, while the capacity +// restricts the time period over which the average actual rate of resource +// consumption approaches the drain rate. +// +///Adding Units +///------------ +// Units can be added to a leaky bucket by either submitting them or reserving +// them. Submitted units are removed from a leaky bucket at the drain rate, +// while reserved units remain unchanged until they are later either cancelled +// (removed from the leaky bucket) or submitted. +// +///Submitting Units +/// - - - - - - - - +// Units can be submitted to a leaky bucket by invoking the 'submit' method, +// and should be added only after the resource had been consumed. +// +// Figure 1 illustrates a typical workflow for submitting units to a leaky +// bucket. +//.. +// Fig. 1: Capacity = 5 units, Rate = 1 unit / second +// +// Submit 5 Submit 2 +// +// 7| | 7| | 7| | 7| | +// 6| | 6| | 6| | 6| | +// c--5|~~~~~| c--5|-----| c--5|-----| c--5|-----| +// 4|~~~~~| 4| | 4| | 4| | +// 3|~~~~~| 3| | 3|~~~~~| 3| | +// 2|~~~~~| 2| | 2|~~~~~| 2| | +// 1|~~~~~| 1|~~~~~| 1|~~~~~| 1| | +// +-- --+ +-- --+ +-- --+ +-- --+ +// +// Time: t0 t0 + 4s t0 + 4s t0 + 10s +//.. +// Suppose that we have an empty leaky bucket with a capacity of 'c = 5 units' +// and a drain rate of 'd = 1 units/s'. At 't0', we submit 5 units to the +// leaky bucket, bringing the total number of units held up to 5. At 't0 + +// 4s', 4 units have been drained from the leaky bucket, bringing the number of +// units held down to 1. Finally, at 't0 + 10s', all units have been drained +// from the leaky bucket, making it empty. +// +// Unlike a real-life water bucket, units submitted to a leaky bucket don't +// spillover after its capacity has been exceeded, instead the leaky bucket may +// hold a number of units beyond its capacity, as examined in Figure 2 below., +// these units are still contained in the leaky bucket. +// +// Figure 2 illustrates what happens if a leaky bucket exceeds its capacity. +// This scenario is the same as that in Figure 1, but at time 't0 + 4s', we +// submit 6 units instead of 2. +//.. +// Fig. 2: Capacity = 5 units, Rate = 1 unit / second +// +// Submit 5 Submit 6 +// +// 7| | 7| | 7|~~~~~| 7| | +// 6| | 6| | 6|~~~~~| 6| | +// c--5|~~~~~| c--5|-----| c--5|~~~~~| c--5|-----| +// 4|~~~~~| 4| | 4|~~~~~| 4| | +// 3|~~~~~| 3| | 3|~~~~~| 3| | +// 2|~~~~~| 2| | 2|~~~~~| 2| | +// 1|~~~~~| 1|~~~~~| 1|~~~~~| 1|~~~~~| +// +-- --+ +-- --+ +-- --+ +-- --+ +// +// Time: t0 t0 + 4s t0 + 4s t0 + 10s +//.. +// At 't0 + 4s', when the number of units held by the leaky bucket is 1, we +// submit 6 more units. This brings the number of units held to 7, which +// exceeds the capacity of the leaky bucket. At 't0 + 10s', 6 units had been +// drained from the leaky bucket bring the number of units held down to 1. +// +///Reserving Units +///- - - - - - - - +// Units can be reserved for a leaky bucket using the 'reserve' method, and +// they may be later canceled using the 'cancelReserved' method or submitted +// using the 'submitReserved' method. Unlike submitted units, reserved units +// do *not* drain from the leaky bucket; like submitted units, reserved units +// count toward the total number of units for the purposes of determining +// whether a leaky bucket has exceeded its capacity. Reserving units +// effectively decreases the capacity of a leaky bucket. Therefore, the time +// interval between reserving units and submitting or canceling the reservation +// should be kept as short as possible. For a practical example of using +// reserved units, please see 'balb_reservationguard'. +// +// Figure 3 illustrate an example of how reserving units works in a leaky +// bucket. +//.. +// Fig. 3: Capacity = 5 units, Rate = 1 unit / second +// +// Reserve 4 Submit 3 Cancel 1 +// from reserve from reserve +// +// 7| | 7| | 7| | 7| | 7| | +// 6| | 6| | 6| | 6| | 6| | +// c--5|-----| c--5|-----| c--5|-----| c--5|-----| c--5|-----| +// 4|#####| 4|#####| 4|~~~~~| 4| | 4| | +// 3|#####| 3|#####| 3|~~~~~| 3| | 3| | +// 2|#####| 2|#####| 2|~~~~~| 2| | 2| | +// 1|#####| 1|#####| 1|#####| 1|#####| 1| | +// +-- --+ +-- --+ +-- --+ +-- --+ +-- --+ +// +// Time: t0 t0 + 5s t0 + 6s t0 + 9s t0 + 10s +//.. +// Suppose that we have an empty leaky bucket with a capacity of 'c = 5 units' +// and a drain rate of 'd = 1 units/s'. At 't0' we reserve 4 units. At 't0 + +// 5s', we observe that none of the reserved units are drained from the leaky +// bucket. At 't0 + 6s', we submit 3 of the previously reserved units, which +// brings the number of reserved units down to 1 and the number of units held +// up to 3. At 't0 + 9s', we observe that all but the remaining reserved unit +// have been drained from the bucket. Finally, at 't0 + 10s', we cancel the +// remaining reserved unit. +// +///Monitoring Resource Usage +///------------------------- +// The recommended usage of a leaky bucket is to first check whether 1 unit can +// be added without causing the leaky bucket to overflow, and if so, consume +// the desired amount of the resource. Afterwards, submit the amount of +// consumed resource to the leaky bucket. +// +///Checking for Overflow +///- - - - - - - - - - - +// A leaky bucket can be queried whether submitting a 1 more unit would cause +// it to overflow via the 'wouldOverflow' method. This method facilitates the +// recommended usage of a leak bucket: check whether 1 more unit can be added +// without causing the leaky bucket to overflow, and if so, consume the desired +// amount of the resource (which may be more than 1). Compared to the +// alternative -- checking whether the desired amount can be submitted without +// overflow -- this recommended usage may allow a limited spike in the rate of +// actual consumption when the leaky bucket is empty (which is often +// acceptable) but is able to sustain a long term average that is actually +// closer to the drain rate. +// +///Modeling a Network Connection +///----------------------------- +// The primary use case of leaky bucket is limiting the rate at which data is +// written on a network. In this use case, the drain rate of the bucket +// corresponds to the *ideal* maximum transmission rate that the client wishes +// to enforce on their outgoing connection. Clients may choose to provide a +// value related to the physical limitations of their network or any other +// arbitrary limit. The function of a leaky bucket's capacity is to limit the +// time period over which the average actual transmission rate may exceed the +// configured drain rate of the leaky bucket (see 'Approximations' section and +// 'Sliding Time-Window' section). +// +///Approximations +///-------------- +// Leaky bucket is modeled on a water bucket with a hole, but as a leaky bucket +// does not manage any resources, there are several approximations to this +// model: +// +//: 1 Units are submitted instantaneously to the leaky bucket, whereas the +//: consumption of a resource occurs over time at a rate that depends on the +//: nature and speed of the resource. +//: +//: 2 Leaky bucket simulates the consumption of a resource with a specified +//: fixed drain rate, but the resource is actually consumed at different +//: rates over time. This approximation still guarantees that the actual +//: consumption rate does not exceed the specified drain rate when amortized +//: over some configured period of time (determined by the capacity and the +//: drain rate of the bucket), but does not prevent the consumption rate from +//: spiking above the drain rate for shorter periods of time (see 'Sliding +//: Time-Window' section). +// +///Sliding Time-Window +///------------------- +// One of the properties of the resource pattern created by using a leaky +// bucket is an approximation of a sliding time window over which the average +// consumption rate is guaranteed to be less than the drain rate. This time +// period can be calculated using the leaky bucket's capacity and drain rate, +// which can be conveniently performed using the 'calculateTimeWindow' class +// method. +// +///Time Synchronization +///-------------------- +// Leaky bucket does not utilize an internal timer, so timing must be handled +// manually. Clients can specify an initial time interval for the leaky bucket +// at construction or using the 'reset' method. Whenever the number of units +// in a leaky bucket needs to be updated, clients must invoke the 'updateState' +// method specifying the current time interval. Since leaky bucket cares only +// about the elapsed time (not absolute time), the specified time intervals may +// be relative to any arbitrary time origin, though all of them must refer to +// the same origin. For the sake of consistency, clients are encouraged to use +// the unix epoch time (such as the values returned by +// 'bdlt::CurrentTime::now'). +// +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Controlling Network Traffic Generation +///------------------------------------------------- +// In some systems, data is processed faster than they are consumed by I/O +// interfaces. This could lead to data loss due to the overflowing of the +// buffers where data is queued before being processed. In other systems, +// generic resources are shared, and their consumption might need to be managed +// in order to guarantee quality-of-service (QOS). +// +// Suppose we have a network interface capable of transferring at a rate of +// 1024 byte/s and an application wants to transmit 5 KB (5120 bytes) of data +// over that network in 20 different 256-bytes data chunks. We want to send +// data over this interface and want to ensure the transmission uses on average +// less than 50% of the available bandwidth, or 512 byte/s. In this way, other +// clients can still reasonably send and receive data using the same network +// interface. +// +// Further suppose that we have a function, 'sendData', that transmits a +// specified data buffer over that network interface: +//.. +// bool sendData(const char *buffer, size_t dataSize); +// // Send the specified 'buffer' of the specified size 'dataSize' through +// // the network interface. Return 'true' if data was sent successfully, +// // and 'false' otherwise. +//.. +// First, we create a leaky bucket having a drain rate of 512 bytes/s, a +// capacity of 2560 bytes, and a time origin set to the current time (as an +// interval from unix epoch). Note that 'unit', the unit of measurement for +// leaky bucket, corresponds to 'byte' in this example: +//.. +// bsls::Types::Uint64 rate = 512; // bytes/second +// bsls::Types::Uint64 capacity = 2560; // bytes +// bsls::TimeInterval now = bdlt::CurrentTime::now(); +// LeakyBucket bucket(rate, capacity, now); +//.. +// Then, we define a data buffer to be sent, the size of each data chunk, and +// the total size of the data to transmit: +//.. +// char buffer[5120]; +// unsigned int chunkSize = 256; // in bytes +// bsls::Types::Uint64 totalSize = 20 * chunkSize; // in bytes +// bsls::Types::Uint64 dataSent = 0; // in bytes +// +// // Load 'buffer'. +// // ... +//.. +// Notice that, for the sake of brevity, we elide the loading of 'buffer' with +// the data to be sent. +// +// Now, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would cause the leaky bucket to overflow. +// If not, we send an additional chunk of data and submit the number of bytes +// sent to the leaky bucket. Note that 'submit' is invoked only after the data +// has been sent. +//.. +// char *data = buffer; +// while (dataSent < totalSize) { +// now = bdlt::CurrentTime::now(); +// if (!bucket.wouldOverflow(now)) { +// if (true == sendData(data, chunkSize)) { +// data += chunkSize; +// bucket.submit(chunkSize); +// dataSent += chunkSize; +// } +// } +//.. +// Finally, if submitting another byte will cause the leaky bucket to overflow, +// then we wait until the submission will be allowed by waiting for an amount +// time returned by the 'calculateTimeToSubmit' method: +//.. +// else { +// bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit( +// now); +// +// // Round up the number of microseconds. +// bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + +// ((timeToSubmit.nanoseconds() % 1000) ? 1 : 0); +// bslmt::ThreadUtil::microSleep(static_cast(uS)); +// } +// } +//.. +// Notice that we wait by putting the thread into a sleep state instead of +// using busy-waiting to better optimize for multi-threaded applications. + +#include + +#include +#include +#include + +#include + +namespace BloombergLP { +namespace balb { + + //================== + // class LeakyBucket + //================== + +class LeakyBucket { + // This mechanism implements a leaky bucket that allows clients to monitor + // whether a resource is being consumed at a particular rate. The behavior + // of a leak bucket is determined by two properties: the drain rate (in + // units/s) and capacity (in units), both of which can be specified at + // construction or using the 'setRateAndCapacity' method. + // + // Units can be added to a leaky bucket by either submitting them using the + // 'submit' method or reserving them using the 'reserve' method. Submitted + // units are removed from a leaky bucket at the drain rate, while reserved + // units stays unaffected in a leaky bucket until they are either cancelled + // (removed from the leaky bucket) using the 'cancelReserved' method or + // submitted using the 'submitReserved' method. + // + // Adding units to a leaky bucket will cause it to overflow if after the + // units are added, the total number of units in the leaky bucket + // (including both submitted and reserved units) exceeds its capacity. A + // leaky bucket can be queried whether adding a specified number of units + // would cause it to overflow via the 'wouldOverflow' method. If + // submitting units to a leaky bucket will cause it to overflow, the + // estimated amount of time to wait before 1 more units can be submitted + // without causing the leaky bucket to overflow can be determined using the + // 'calculateTimeToSubmit' method. + // + // The state of a leaky bucket must be updated manually using the + // 'updateState' method supplying the current time interval. The time + // intervals supplied should all refer to the same time origin. + // + // A leaky bucket keeps some statistics, including the number of submitted + // units, that can be accessed using the 'getStatistics' method and reset + // using the 'resetStatistics' method. + // + // The class invariants are: + //: o 'capacity() > 0' + //: o 'drainRate() > 0' + // + // This class: + //: o is *exception* *neutral* (agnostic) + //: o is *const* *thread-safe* + // For terminology see 'bsldoc_glossary'. + + // DATA + bsls::Types::Uint64 d_drainRate; // drain rate in units per + // second + + bsls::Types::Uint64 d_capacity; // the bucket capacity in units + + bsls::Types::Uint64 d_unitsReserved; // reserved units + + bsls::Types::Uint64 d_unitsInBucket; // number of units currently in + // the bucket + + bsls::Types::Uint64 d_fractionalUnitDrainedInNanoUnits; + // fractional number of units + // that is carried from the + // last drain operation + + bsls::TimeInterval d_lastUpdateTime; // time of last drain, updated + // via the 'updateState' method + + bsls::TimeInterval d_maxUpdateInterval; // time to drain maximum number + // of units + + bsls::Types::Uint64 d_statSubmittedUnits; // submitted unit counter, + // number of submitted units + // since last reset + + bsls::Types::Uint64 d_statSubmittedUnitsAtLastUpdate; + // submitted unit counter saved + // during last update + + bsls::TimeInterval d_statisticsCollectionStartTime; + // start time for the submitted + // unit counter + + private: + // NOT IMPLEMENTED + LeakyBucket& operator=(const LeakyBucket&); + LeakyBucket(const LeakyBucket&); + + public: + // CLASS METHODS + static bsls::Types::Uint64 calculateCapacity( + bsls::Types::Uint64 drainRate, + const bsls::TimeInterval& timeWindow); + // Return the capacity of a leaky bucket as the rounded-down product of + // the specified 'drainRate' by the specified 'timeWindow'. If the + // result evaluates to 0, return 1. The behavior is undefined unless + // the product of 'drainRate' and 'timeWindow' can be represented by a + // 64-bit unsigned integral type. + + static bsls::TimeInterval calculateDrainTime(bsls::Types::Uint64 numUnits, + bsls::Types::Uint64 drainRate, + bool ceilFlag); + // Return the time interval required to drain the specified 'numUnits' + // at the specified 'drainRate', round up the number of nanoseconds in + // the time interval if the specified 'ceilFlag' is set to 'true', + // otherwise, round down the number of nanoseconds. The behavior is + // undefined unless the number of seconds in the calculated interval + // may be represented by a 64-bit signed integral type. + + static bsls::TimeInterval calculateTimeWindow( + bsls::Types::Uint64 drainRate, + bsls::Types::Uint64 capacity); + // Return the time interval over which a leaky bucket *approximates* a + // moving-total of submitted units, as the rounded-down ratio between + // the specified 'capacity' and the specified 'drainRate'. If the + // rounded ratio is 0, return a time interval of 1 nanosecond. The + // behavior is undefined unless 'drainRate > 0' and + // 'capacity / drainRate' can be represented with 64-bit signed + // integral type. + + // CREATORS + LeakyBucket(bsls::Types::Uint64 drainRate, + bsls::Types::Uint64 capacity, + const bsls::TimeInterval& currentTime); + // Create an empty leaky bucket having the specified 'drainRate', the + // specified 'capacity', and the specified 'currentTime' as the initial + // 'lastUpdateTime'. The behavior is undefined unless '0 < newRate', + // '0 < newCapacity', and 'LLONG_MIN != currentTime.seconds()'. + + ~LeakyBucket(); + // Destroy this object. + + // MANIPULATORS + bsls::TimeInterval calculateTimeToSubmit( + const bsls::TimeInterval& currentTime); + // If 1 more unit can be submitted to this leaky bucket without causing + // it to overflow, then return a time interval of 0 immediately. + // Otherwise, first update the state of this leaky bucket to the + // specified 'currentTime'. Then, return the estimated time interval + // that should pass from 'currentTime' until 1 more unit can be + // submitted to this leaky bucket without causing it to overflow. The + // number of nanoseconds in the returned time interval is rounded up. + // Note that a time interval of 0 can still be return after the state + // of this leaky bucket has been updated to 'currentTime'. Also note + // that after waiting for the returned time interval, clients should + // typically check again using this method, because additional units + // may have been submitted in the interim. The behavior is undefined + // unless 'LLONG_MIN != currentTime.seconds()' and the total number of + // seconds in the time interval resulting from + // 'currentTime - lastUpdateTime()' can be represented with a 64-bit + // signed integer. + + void cancelReserved(bsls::Types::Uint64 numUnits); + // Cancel the specified 'numUnits' that were previously reserved. This + // method reduces the number of reserved units by 'numUnits'. The + // behavior is undefined unless 'numUnits <= unitsReserved()'. + + void reserve(bsls::Types::Uint64 numUnits); + // Reserve the specified 'numUnits' for future use by this leaky + // bucket. The behavior is undefined unless 'unitsReserved() + + // unitsInBucket() + numOfUnits' can be represented by a 64-bit + // unsigned integral type. Note that after this operation, this bucket + // may overflow. Also note that the time interval between the + // invocations of 'reserve' and 'submitReserved' or 'cancelReserved' + // should be kept as short as possible; otherwise, the precision of the + // time interval calculated by 'calculateTimeToSubmit' may be + // negatively affected. + + void reset(const bsls::TimeInterval& currentTime); + // Reset the the following statistic counters for this leaky bucket to + // 0: 'unitsInBucket', 'unitsReserved', 'submittedUnits', and + // 'unusedUnits'. Set the 'lastUpdateTime' and the + // 'statisticCollectionStartTime' to the specified 'currentTime' of + // this leaky bucket. The behavior is undefined unless + // 'LLONG_MIN != currentTime.seconds()'. + + void resetStatistics(); + // Reset the statics collected for this leaky bucket by setting the + // number of units used and the number of units submitted to 0, and set + // the 'statisticsCollectionStartTime' to the 'lastUpdateTime' of this + // leaky bucket. + + void setRateAndCapacity(bsls::Types::Uint64 newRate, + bsls::Types::Uint64 newCapacity); + // Set the drain rate of this leaky bucket to the specified 'newRate' + // and the capacity of this leaky bucket to the specified + // 'newCapacity'. The behavior is undefined unless '0 < newRate' and + // '0 < newCapacity'. + + void submit(bsls::Types::Uint64 numUnits); + // Submit the specified 'numUnits' to this leaky bucket. The behavior + // is undefined unless 'unitsReserved() + unitsInBucket() + numUnits' + // can be represented by a 64-bit unsigned integral type. Note that + // after this operation, this leaky bucket may overflow. + + void submitReserved(bsls::Types::Uint64 numUnits); + // Submit the specified 'numUnits' that were previously reserved. This + // method reduces the number of reserved units by 'numUnits' and + // submits 'numUnits' to this leaky bucket. The behavior is undefined + // unless 'numUnits <= unitsReserved()'. + + void updateState(const bsls::TimeInterval& currentTime); + // Set the 'lastUpdateTime' of this leaky bucket to the specified + // 'currentTime'. If 'currentTime' is after 'lastUpdateTime', then + // update the 'unitsInBucket' of this leaky bucket by subtracting from + // it the number of units drained from 'lastUpdateTime' to + // 'currentTime'. If 'currentTime' is before the + // 'statisticsCollectionStartTime' of this leaky bucket, set + // 'statisticsCollectionStartTime' to 'currentTime'. The behavior is + // undefined unless 'LLONG_MIN != currentTime.seconds()' and the total + // number of seconds in the time interval resulting from + // 'currentTime - lastUpdateTime()' can be represented with a 64-bit + // signed integer. + + bool wouldOverflow(const bsls::TimeInterval& currentTime); + // Update the state of this this leaky bucket to the specified + // 'currentTime', and return 'true' if adding 1 more unit to this leaky + // bucket would cause the total number of units held by this leaky + // bucket to exceed its capacity, and 'false' otherwise. Note that + // this method counts both submitted units and reserved units toward + // the total number of units held by this leaky bucket. The behavior + // is undefined unless 'LLONG_MIN != currentTime.seconds()' and the + // total number of seconds in the time interval resulting from + // 'currentTime - lastUpdateTime()' can be represented with a 64-bit + // signed integer. + + // ACCESSORS + bsls::Types::Uint64 capacity() const; + // Return the capacity of this leaky bucket. + + bsls::Types::Uint64 drainRate() const; + // Return the drain rate of this leaky bucket. + + void getStatistics(bsls::Types::Uint64* submittedUnits, + bsls::Types::Uint64* unusedUnits) const; + // Load, into the specified 'submittedUnits' and the specified + // 'unusedUnits' respectively, the numbers of submitted units and the + // number of unused units for this leaky bucket from the + // 'statisticsCollectionStartTime' to the 'lastUpdateTime'. The number + // of unused units is the difference between the number of units that + // could have been consumed and the number of units actually submitted + // for the time period. + + bsls::TimeInterval lastUpdateTime() const; + // Return the time interval when this leaky bucket was last updated. + + bsls::TimeInterval statisticsCollectionStartTime() const; + // Return the time interval when the collection of the statistics (as + // returned by 'getStatistics') started. + + bsls::Types::Uint64 unitsInBucket() const; + // Return the number of submitted units in this leaky bucket. + + bsls::Types::Uint64 unitsReserved() const; + // Return the number of reserved units in this leaky bucket. +}; + +// ============================================================================ +// INLINE FUNCTION DEFINITIONS +// ============================================================================ + + //----------------------- + // class LeakyBucket + //----------------------- + +// CREATORS +inline +LeakyBucket::~LeakyBucket() +{ + BSLS_ASSERT_SAFE(0 < d_drainRate); + BSLS_ASSERT_SAFE(0 < d_capacity); +} + +// MANIPULATORS +inline +void LeakyBucket::cancelReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); + + if (numUnits > d_unitsReserved) { + d_unitsReserved = 0; + } + else { + d_unitsReserved -= numUnits; + } +} + +inline +void LeakyBucket::reserve(bsls::Types::Uint64 numUnits) +{ + // Check whether adding 'numUnits' causes an unsigned 64-bit integral type + // to overflow. + + BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsReserved); + + BSLS_ASSERT_SAFE( + d_unitsInBucket <= ULLONG_MAX - d_unitsReserved - numUnits); + + d_unitsReserved += numUnits; +} + +inline +void LeakyBucket::reset(const bsls::TimeInterval& currentTime) +{ + BSLS_ASSERT_SAFE(LLONG_MIN != currentTime.seconds()); + + d_lastUpdateTime = currentTime; + d_unitsInBucket = 0; + d_unitsReserved = 0; + resetStatistics(); +} + +inline +void LeakyBucket::resetStatistics() +{ + d_statisticsCollectionStartTime = d_lastUpdateTime; + d_statSubmittedUnits = 0; + d_statSubmittedUnitsAtLastUpdate = 0; +} + +inline +void LeakyBucket::submit(bsls::Types::Uint64 numUnits) +{ + // Check whether adding 'numUnits' causes an unsigned 64-bit integer type + // to overflow. + + BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - d_unitsInBucket); + + BSLS_ASSERT_SAFE( + d_unitsReserved <= ULLONG_MAX - d_unitsInBucket - numUnits); + + d_unitsInBucket += numUnits; + d_statSubmittedUnits += numUnits; +} + +inline +void LeakyBucket::submitReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); + + d_unitsReserved -= numUnits; + + submit(numUnits); +} + +// ACCESSORS +inline +bsls::Types::Uint64 LeakyBucket::capacity() const +{ + return d_capacity; +} + +inline +bsls::Types::Uint64 LeakyBucket::drainRate() const +{ + return d_drainRate; +} + +inline +bsls::TimeInterval LeakyBucket::lastUpdateTime() const +{ + return d_lastUpdateTime; +} + +inline +bsls::TimeInterval LeakyBucket::statisticsCollectionStartTime() const +{ + return d_statisticsCollectionStartTime; +} + +inline +bsls::Types::Uint64 LeakyBucket::unitsInBucket() const +{ + return d_unitsInBucket; +} + +inline +bsls::Types::Uint64 LeakyBucket::unitsReserved() const +{ + return d_unitsReserved; +} + +} // close package namespace +} // close enterprise namespace + +#endif + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_leakybucket.t.cpp b/groups/bal/balb/balb_leakybucket.t.cpp new file mode 100644 index 0000000000..4e6b0bfc4f --- /dev/null +++ b/groups/bal/balb/balb_leakybucket.t.cpp @@ -0,0 +1,3077 @@ +// balb_leakybucket.t.cpp -*-C++-*- + +#include + +#include + +#include + +#include + +#include +#include + +using namespace BloombergLP; +using namespace bsl; + +//============================================================================= +// TEST PLAN +//----------------------------------------------------------------------------- +// Overview +// -------- +// The component under test implements a mechanism. +// +// This class provides a value constructor capable of creating an object +// having any parameters. +// +// Primary Manipulators: +//: o Value constructor +// +// Basic Accessors: +// o 'rate' +// o 'capacity' +// o 'lastUpdateTime' +// o 'unitsInBucket' +// o 'unitsReserved' +// +// Global Concerns: +//: o ACCESSOR methods are declared 'const'. +//: o CREATOR & MANIPULATOR pointer/reference parameters are declared 'const'. +//: o Precondition violations are detected in appropriate build modes. +// +// Global Assumptions: +//: o ACCESSOR methods are 'const' thread-safe. +//----------------------------------------------------------------------------- +// CLASS METHODS +// [17] static bsls::TimeInterval calculateTimeWindow(capacity,drainRate); +// [16] static bsls::Types::Uint64 calculateCapacity(drainRate, timeWindow); +// [14] calculateDrainTime(numOfUnits, drainRate, ceilFlag); +// +// CREATORS +// [ 3] LeakyBucket(drainRate, window, currentTime); +// +// MANIPULATORS +// [ 6] void setRateAndCapacity(newRate, newCapacity); +// [ 5] void submit(bsls::Types::Uint64 numOfUnits); +// [ 5] void reserve(bsls::Types::Uint64 numOfUnits); +// [ 7] void updateState(const bsls::TimeInterval& currentTime); +// [ 8] bool wouldOverflow(currentTime); +// [10] void submitReserved(bsls::Types::Unit64 numOfUnits); +// [10] void cancelReserved(bsls::Types::Unit64 numOfUnits); +// [12] void resetStatistics(); +// [13] void reset(const bsls::TimeInterval& currentTime); +// [15] bsls::TimeInterval calculateTimeToSubmit(currentTime); +// +// ACCESSORS +// [ 4] bsls::Types::Uint64 drainRate() const; +// [ 4] bsls::Types::Uint64 capacity() const; +// [ 4] bsls::Types::Uint64 unitsInBucket() const; +// [ 4] bsls::Types::Uint64 unitsReserved() const; +// [ 4] bsls::TimeInterval lastUpdateTime() const; +// [11] void getStatistics(smtUnits, unusedUnits) const; +//----------------------------------------------------------------------------- +// [ 1] BREATHING TEST +// [19] USAGE EXAMPLE +// [ 4] All accessor methods are declared 'const'. +// [ *] All creator/manipulator ptr./ref. parameters are 'const'. +//============================================================================= + +// ============================================================================ +// BTES_RESERVATIOGUARD TEST HELPER +// ---------------------------------------------------------------------------- + +class mock_LB { + // This mechanism mocks basic functionality of 'balb::LeakyBucket' and is + // used to test the 'testLB' function. + + // DATA + bsls::Types::Uint64 d_unitsInBucket; // number of units currently in the + // bucket + + bsls::TimeInterval d_lastUpdateTime; // time of the last update + + bsls::TimeInterval d_submitInterval; // minimum interval between + // submitting units + + bsls::Types::Uint64 d_rate; // drain rate in units per second. + // Used for 'setRateAndCapacity' mock + + bsls::Types::Uint64 d_capacity; // capacity in units. Used for + // 'setRateAndCapacity' mock + public: + // CREATORS + mock_LB(); + // Create a 'mock_LB' object having rate of 1 unit per second, capacity + // of 1 unit, lastUpdateTime of 0, submit interval of 0, and such that + // 'unitsInBucket == 0'. + + mock_LB(bsls::TimeInterval submitInterval, bsls::TimeInterval currentTime); + // Create a 'mock_LB' object, having rate of 1 unit per second, + // capacity of 1, the specified 'submitInterval' the lastUpdateTime of + // the specified 'currentTime' and such that 'unitsInBucket == 0'. + + // MANIPULATORS + void setRateAndCapacity(bsls::Types::Uint64 newRate, + bsls::Types::Uint64 newCapacity); + // Set the rate of this 'mock_LB' object to the specified 'newRate' and + // the capacity to the specified 'newCapacity'. + + void reset(bsls::TimeInterval currentTime); + // Set the 'lastUpdateTime' of this 'mock_LB' object to the specified + // 'currentTime' and number of units in bucket to 0. + + void submit(bsls::Types::Uint64 numOfUnits); + // Add the specified 'numOfUnits' to the 'unitsInBucket' counter. + + bool wouldOverflow(bsls::TimeInterval currentTime); + // Return whether submitting 1 more unit at the specified 'currentTime' + // would cause this object overflow. Note that actually, 'mock_LB' + // does not care about the capacity and simply allows submitting if the + // time passed since last check exceeds the specified 'submitInterval' + // and forbids submitting otherwise. + + bsls::TimeInterval calculateTimeToSubmit(bsls::TimeInterval currentTime); + // Return the time interval that should pass until it will be possible + // to submit any new unit into this leaky bucket, at the specified + // 'currentTime'. Return the time interval, calculated as difference + // between time passed since last check and 'submitInterval', if the + // time interval passed since last check is shorter than + // 'submitInterval'. Otherwise, return zero interval. + + // ACCESSORS + bsls::Types::Uint64 unitsInBucket() const; + // Return the number of units that are currently in the bucket. + + bsls::TimeInterval lastUpdateTime() const; + // Return the time of last update of this 'mock_LB' object, as a time + // interval, describing the moment in time the bucket was last updated. + // The returned time interval uses the same reference point as the time + // interval specified during construction or last invocation of the + // 'reset' method. + + bsls::TimeInterval submitInterval() const; + // Return the minimum time interval that must pass between submits. + + bsls::Types::Uint64 rate() const; + // Return the drain rate in units per second. + + bsls::Types::Uint64 capacity() const; + // Return the capacity in units. +}; + +// CREATORS +inline +mock_LB::mock_LB() +: d_unitsInBucket(0) +, d_lastUpdateTime(0,0) +, d_submitInterval(0,0) +, d_rate(1) +, d_capacity(1) +{} + +inline +mock_LB::mock_LB(bsls::TimeInterval submitInterval, + bsls::TimeInterval currentTime) +: d_unitsInBucket(0) +, d_lastUpdateTime(currentTime) +, d_submitInterval(submitInterval) +, d_rate(1) +, d_capacity(1) +{} + +// MANIPULATORS +inline +void mock_LB::setRateAndCapacity(bsls::Types::Uint64 newRate, + bsls::Types::Uint64 newCapacity) +{ + d_rate = newRate; + d_capacity = newCapacity; +} + +inline +void mock_LB::reset(bsls::TimeInterval currentTime) +{ + d_lastUpdateTime = currentTime; + d_unitsInBucket = 0; +} + +inline +void mock_LB::submit(bsls::Types::Uint64 numOfUnits) +{ + d_unitsInBucket += numOfUnits; +} + +inline +bool mock_LB::wouldOverflow(bsls::TimeInterval currentTime) +{ + bsls::TimeInterval delta = currentTime - d_lastUpdateTime; + + if (delta < d_submitInterval) { + return true; // RETURN + } + + d_lastUpdateTime = currentTime; + + return false; +} + +inline +bsls::TimeInterval mock_LB::calculateTimeToSubmit( + bsls::TimeInterval currentTime) +{ + bsls::TimeInterval delta = currentTime - d_lastUpdateTime; + + if (delta < d_submitInterval) { + return d_submitInterval - delta; // RETURN + } + + d_lastUpdateTime = currentTime; + + return bsls::TimeInterval(0); +} + +// ACCESSORS +inline +bsls::Types::Uint64 mock_LB::unitsInBucket() const +{ + return d_unitsInBucket; +} + +inline +bsls::TimeInterval mock_LB::lastUpdateTime() const +{ + return d_lastUpdateTime; +} + +inline +bsls::TimeInterval mock_LB::submitInterval() const +{ + return d_submitInterval; +} + +inline +bsls::Types::Uint64 mock_LB::rate() const +{ + return d_rate; +} + +inline +bsls::Types::Uint64 mock_LB::capacity() const +{ + return d_capacity; +} + +//============================================================================= +// STANDARD BDE ASSERT TEST MACRO +//----------------------------------------------------------------------------- +static int testStatus = 0; + +static void aSsErT(int c, const char *s, int i) +{ + if (c) { + cout << "Error " << __FILE__ << "(" << i << "): " << s + << " (failed)" << endl; + if (testStatus >= 0 && testStatus <= 100) ++testStatus; + } +} + +# define ASSERT(X) { aSsErT(!(X), #X, __LINE__); } +//----------------------------------------------------------------------------- +#define LOOP_ASSERT(I,X) { \ + if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__); }} + +#define LOOP2_ASSERT(I,J,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \ + << J << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP3_ASSERT(I,J,K,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \ + << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP4_ASSERT(I,J,K,L,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \ + #K << ": " << K << "\t" << #L << ": " << L << "\n"; \ + aSsErT(1, #X, __LINE__); } } + +#define LOOP0_ASSERT ASSERT +#define LOOP1_ASSERT LOOP_ASSERT + +//============================================================================= +// STANDARD BDE VARIADIC ASSERT TEST MACROS +//----------------------------------------------------------------------------- + +#define NUM_ARGS_IMPL(X5, X4, X3, X2, X1, X0, N, ...) N +#define NUM_ARGS(...) NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0, "") + +#define LOOPN_ASSERT_IMPL(N, ...) LOOP ## N ## _ASSERT(__VA_ARGS__) +#define LOOPN_ASSERT(N, ...) LOOPN_ASSERT_IMPL(N, __VA_ARGS__) + +#define ASSERTV(...) LOOPN_ASSERT(NUM_ARGS(__VA_ARGS__), __VA_ARGS__) + +// ============================================================================ +// SEMI-STANDARD TEST OUTPUT MACROS +// ---------------------------------------------------------------------------- + +#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value. +#define Q(X) cout << "<| " #X " |>" << endl; // Quote identifier literally. +#define P_(X) cout << #X " = " << (X) << ", " << flush; // 'P(X)' without '\n' +#define T_ cout << "\t" << flush; // Print tab w/o newline. +#define L_ __LINE__ // current Line number + +// ============================================================================ +// NEGATIVE-TEST MACRO ABBREVIATIONS +// ---------------------------------------------------------------------------- + +#define ASSERT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_PASS(EXPR) +#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR) +#define ASSERT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL(EXPR) +#define ASSERT_OPT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR) + +//============================================================================= +// GLOBAL TYPEDEFS/CONSTANTS FOR TESTING +//----------------------------------------------------------------------------- +typedef balb::LeakyBucket Obj; +typedef bsls::TimeInterval Ti; +typedef bsls::Types::Uint64 Uint64; +typedef unsigned int Uint; + +template +static Ti testLB( + T& object, + Uint64 rate, + Uint64 capacity, + Uint64 dataSize, + Uint64 chunkSize, + const Ti& minQueryInterval) + // Simulate load generation on specified rate controlling 'object', having + // the specified 'rate' and 'capacity', by modeling sending the specified + // 'dataSize' divided on the chunks of the specified 'chunkSize', keeping + // intervals between querying the 'object' not shorter than the specified + // 'minQueryInterval'. Return the total amount of time +{ + Uint64 dataSent = 0; + + Ti now(0.0); + Ti begin(now); + + object.setRateAndCapacity(rate, capacity); + + Uint64 numSleeps=0; + Uint64 loops = 0; + + while (dataSent < dataSize) { + ++loops; + + if (object.wouldOverflow(now)) { + + // Query the rate controlling object, how long it should pass until + // submitting more units is allowed. + + Ti timeToSubmit = object.calculateTimeToSubmit(now); + + if (0 != timeToSubmit) { + + // do not query LB with intervals, shorter than the specified + // minimum interval. + + if(timeToSubmit < minQueryInterval) { + timeToSubmit = minQueryInterval; + } + now += timeToSubmit; + ++numSleeps; + } + continue; + } + object.submit(chunkSize); + dataSent += chunkSize; + } + + Ti actual = now - begin; + return actual; +} + +//============================================================================= +// USAGE EXAMPLE +//----------------------------------------------------------------------------- + +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Controlling Network Traffic Generation +///------------------------------------------------- +// In some systems, data is processed faster than they are consumed by I/O +// interfaces. This could lead to data loss due to the overflowing of the +// buffers where data is queued before being processed. In other systems, +// generic resources are shared, and their consumption might need to be managed +// in order to guarantee quality-of-service (QOS). +// +// Suppose we have a network interface capable of transferring at a rate of +// 1024 byte/s and an application wants to transmit 5 KiB (5120 bytes) of data +// over that network in 20 different 256-bytes data chunks. We want to send +// data over this interface and want to ensure the transmission uses on average +// less than 50% of the available bandwidth, or 512 byte/s. In this way, other +// clients can still reasonably send and receive data using the same network +// interface. +// +// Further suppose that we have a function, 'sendData', that transmits a +// specified data buffer over that network interface: +//.. +bool sendData(const char *buffer, size_t dataSize) + // Send the specified 'buffer' of the specified size 'dataSize' through + // the network interface. Return 'true' if data was sent successfully, + // and 'false' otherwise. +{ + (void) buffer; + (void) dataSize; +//.. +// In our example we don`t deal with actual data sending, so we assume that +// the function sends data successfully and return true. +//.. + return true; +} +//.. + +//============================================================================= +// MAIN PROGRAM +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int test = argc > 1 ? atoi(argv[1]) : 0; + bool verbose = argc > 2; + // bool veryVerbose = argc > 3; + // bool veryVeryVerbose = argc > 4; + + cout << "TEST " << __FILE__ << " CASE " << test << endl; + + switch (test) { case 0: + case 19: { + // -------------------------------------------------------------------- + // USAGE EXAMPLE + // The usage example provided in the component header file must + // compile, link, and run on all platforms as shown. + // + // Plan: + // Incorporate usage example from header into driver, remove leading + // comment characters, and replace 'assert' with 'ASSERT'. + // + // Testing: + // USAGE EXAMPLE + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TESTING USAGE EXAMPLE" << endl + << "=====================" << endl; +//.. +// First, we create a leaky bucket having a drain rate of 512 bytes/s, a +// capacity of 2560 bytes, and a time origin set to the current time (as an +// interval from unix epoch). Note that 'unit', the unit of measurement for +// leaky bucket, corresponds to 'byte' in this example: +//.. + bsls::Types::Uint64 rate = 512; // bytes/second + bsls::Types::Uint64 capacity = 2560; // bytes + bsls::TimeInterval now = bdlt::CurrentTime::now(); + balb::LeakyBucket bucket(rate, capacity, now); +//.. +// Then, we define a data buffer to be sent, the size of each data chunk, and +// the total size of the data to transmit: +//.. + char buffer[5120]; + unsigned int chunkSize = 256; // in bytes + bsls::Types::Uint64 totalSize = 20 * chunkSize; // in bytes + bsls::Types::Uint64 dataSent = 0; // in bytes +// +// // Load 'buffer'. +// // ... +//.. +// Notice that, for the sake of brevity, we elide the loading of 'buffer' with +// the data to be sent. +// +// Now, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would cause the leaky bucket to overflow. +// If not, we send an additional chunk of data and submit the number of bytes +// sent to the leaky bucket. Note that 'submit' is invoked only after the data +// has been sent. +//.. + char *data = buffer; + while (dataSent < totalSize) { + now = bdlt::CurrentTime::now(); + if (!bucket.wouldOverflow(now)) { + if (true == sendData(data, chunkSize)) { + data += chunkSize; + bucket.submit(chunkSize); + dataSent += chunkSize; + } + } +//.. +// Finally, if submitting another byte will cause the leaky bucket to overflow, +// then we wait until the submission will be allowed by waiting for an amount +// time returned by the 'calculateTimeToSubmit' method: +//.. + else { + bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit(now); + + // Round up the number of microseconds. + bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + + ((timeToSubmit.nanoseconds() % 1000) ? 1 : 0); + bslmt::ThreadUtil::microSleep(static_cast(uS)); + } + } +//.. +// Notice that we wait by putting the thread into a sleep state instead of +// using busy-waiting to better optimize for multi-threaded applications. +//.. + } break; + case 18: { + // ---------------------------------------------------------------- + // FUNCTIONALITY + // Ensure that 'balb::LeaktBucket' can keep the specified load + // rate when used in real application. + // + // Concerns: + //: 1 'balb::LeakyBucket' keeps specified load rate and allows + //: deviation from the specified rate no bigger than the + //: 'capacity' divided by total amount of sent data. + // + // Plan: + //: 1 Using table-driven technique: + //: + //: 1 Define the set of values, containing values of 'rate' and + //: 'capacity' attributes, size of chunks, data is divided on, + //: test duration and the values of maximum deviation of actual + //: time it took to send data from the specified test + //: duration. + //: + //: 2 Use the 'testLB' function to simulate operations with LB + //: in actual application with different parameters + //: ('rate' and 'capacity'). + //: + //: 3 Verify that that the difference between the specified and + //: measured rate does not exceed allowed limits. + // + // Testing: + // void submit(unsigned int numOfUnits); + // bool wouldOverflow(currentTime); + // bsls::TimeInterval calculateTimeToSubmit(currentTime); + // ---------------------------------------------------------------- + + if (verbose) cout << endl + << "FUNCTIONALITY" << endl + << "=============" << endl; + + const Ti CREATION_TIME; + const Uint64 M = 1024*1024; + const Uint64 G = 1024LL*1024*1024; + + struct { + int d_line; + Uint64 d_drainRate; + Uint64 d_capacity; + Uint64 d_chunkSize; + Uint64 d_dataSize; + Ti d_expectedDuration; + } DATA[] = { + + // LINE RATE CAPACITY CHUNK DATA_SIZE EXP_DUR + // ---- ---- --------- ----- --------- ------- + { L_, 1*M, 1000, 1000, 1*M, Ti( 1)}, + { L_, 1*M, 10000, 1000, 1*M*10, Ti(10)}, + + // 10 MUnits/second rate + { L_, 10*M, 10000, 1000, 10*M*10, Ti(10)}, + { L_, 10*M, 100000, 1500, 10*M*10, Ti(10)}, + + // Low rate + { L_, 50, 5, 10, 50*15, Ti(15)}, + { L_, 50, 5, 5, 50*15, Ti(15)}, + { L_, 10, 1, 2, 10*10, Ti(10)}, + + // high rate (35 G Units/second) + { L_, 35LL*G, 4*M, 1400, 35LL*G, Ti( 1)} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti=0; ti < NUM_DATA; ti++) { + + const int LINE = DATA[ti].d_line; + const Uint64 RATE = DATA[ti].d_drainRate; + const Uint64 CAPACITY = DATA[ti].d_capacity; + const Uint64 CHUNK_SIZE = DATA[ti].d_chunkSize; + const Uint64 DATA_SIZE = DATA[ti].d_dataSize; + const Ti EXP_DURATION = DATA[ti].d_expectedDuration; + + Obj x(1, 1, Ti(0)); + Ti actualDuration = testLB(x, + RATE, + CAPACITY, + DATA_SIZE, + CHUNK_SIZE, + Ti(0,5000)); + + // Calculate the deviation in percentage. + double maxNegDev = + -static_cast((CAPACITY * 100) / DATA_SIZE); + + double dev = 100 * + (EXP_DURATION.totalSecondsAsDouble() - + actualDuration.totalSecondsAsDouble()) + / EXP_DURATION.totalSecondsAsDouble(); + + // Check if the allowed speed was exceeded. + if (dev < 0) { + LOOP_ASSERT(LINE, dev >= maxNegDev); + } + else { + LOOP_ASSERT(LINE, dev <= 2.5); + } + } + } break; + case 17: { + // -------------------------------------------------------------------- + // CLASS METHOD 'calculateTimeWindow' + // Ensure that the class method calculates the equivalent time + // window, using the specified 'capacity' and 'drainRate'. + // + // Concerns: + //: 1 The method correctly calculates the time window using given rate + //: and capacity. + //: + //: 2 The time window is calculated with +1 ns precision. + //: + //: 3 If the calculated time window is 0, the time interval of 1 + //: nanosecond is returned. + //: + //: 4 QoI: Asserted precondition violations are detected when enabled. + // + // Plan: + //: 1 Using the table-driven technique: + //: + //: 1 Specify a set of values, containing values of drain rate, + //: capacity, and the expected value of corresponding time window + //: including boundary values corresponding to every range of + //: values that each individual attribute can independently attain. + //: + //: 2 Additionally provide values, that would allow to test rounding. + //: (C-2..3) + //: + //: 2 For each row of the table, defined in P-1 invoke the + //: 'calculateTimeWindow' class method and verify the returned value. + //: (C-1) + //: + //: 3 Verify that, in appropriate build modes, defensive checks are + //: triggered when an attempt is made to call this function with + //: invalid parameters (using the 'BSLS_ASSERTTEST_*' macros). (C-4) + // + // Testing: + // static bsls::TimeInterval calculateTimeWindow(capacity,drainRate); + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "'calculateTimeWindow'" << endl + << "=====================" << endl; + + const Uint64 MAX_R = ULLONG_MAX; + const Uint64 M = 1000000; + const Uint64 G = 1000000000; + + // Numbers of units to be consumed during different intervals + // at maximum rate. + + const Uint64 U_NS = MAX_R / G; + + struct { + Uint d_line; + Uint64 d_drainRate; + Uint64 d_capacity; + Ti d_expectedWindow; + + } DATA[] = { + + // LINE RATE CAP EXP_WINDOW + // ---- ----------- ----------- ------------------------- + {L_, 1000, 100, Ti( 0, 100000000)}, + {L_, 10*M, 21, Ti( 0, 2100)}, + {L_, 10*M, 1, Ti( 0, 100)}, + + {L_, MAX_R, 1, Ti( 0, 1)}, + {L_, 1, LLONG_MAX, Ti(LLONG_MAX, 0)}, + {L_, MAX_R, ULLONG_MAX, Ti( 1, 0)}, + {L_, MAX_R, U_NS, Ti( 0, 1)} + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti = 0; ti < NUM_DATA; ti++) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 CAPACITY = DATA[ti].d_capacity; + const Uint64 RATE = DATA[ti].d_drainRate; + const Ti EXPECTED_WINDOW = DATA[ti].d_expectedWindow; + + LOOP_ASSERT(LINE, EXPECTED_WINDOW == + Obj::calculateTimeWindow(RATE, CAPACITY)); + } + + // C-4 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + // Rate is 0. + ASSERT_FAIL(Obj::calculateTimeWindow(0, 0)); + ASSERT_FAIL(Obj::calculateTimeWindow(0, 1000)); + + // The resulting time interval can not be represented by + // bsls::TimeInterval. + + ASSERT_FAIL(Obj::calculateTimeWindow(1, ULLONG_MAX)); + } + } break; + case 16: { + // -------------------------------------------------------------------- + // CLASS METHOD 'calculateCapacity' + // Ensure that the class method calculates the equivalent capacity of + // the leaky bucket, using specified 'drainRate' and 'timeWindow'. + // + // Concerns: + //: 1 The method calculates capacity using given rate and time window. + //: + //: 2 The calculated capacity is rounded down. + //: + //: 3 If calculated capacity is 0, the capacity of 1 is returned. + //: + //: 4 QoI: Asserted precondition violations are detected when enabled. + // + //:lan: + //: 1 Using the table-driven technique: + //: + //: 1 Specify a set of values, containing values os drain rate, time + //: window and the expected value of the capacity, including + //: boundary values corresponding to every range of values that + //: each individual attribute can independently attain. + //: + //: 2 Additionally provide values, that would allow to test rounding. + //: (C-2..3) + //: + //: 2 For each row of the table, defined in P-1 invoke the + //: 'calculateCapacity' class method and verify the returned value. + //: (C-1) + //: + //: 3 Verify that, in appropriate build modes, defensive checks are + //: triggered when an attempt is made to call this function with + //: invalid parameters (using the 'BSLS_ASSERTTEST_*' + //: macros). (C-4) + // + // Testing: + // static Uint64 calculateCapacity(drainRate, timeWindow); + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "'calculateCapacity'" << endl + << "===================" << endl; + + const Uint64 MAX_R = ULLONG_MAX; + const Uint64 G = 1000000000; + + // Numbers of units to be consumed during different intervals + // at maximum rate. + const Uint64 U_NS = MAX_R / G; + + struct { + Uint d_line; + Uint64 d_drainRate; + Ti d_timeWindow; + Uint64 d_expectedCapacity; + } DATA[] = { + // LINE RATE WINDOW EXP_CAPACITY + // ---- ----- ----------------------- -------------- + {L_, 1000, Ti( 2, 500000000), 2500}, + // C-1 + {L_, 10, Ti( 2, 110000000), 21}, + // C-2 + {L_, 5, Ti( 0, 10000000), 1}, + // C-3 + {L_, 1, Ti(LLONG_MAX, 0), LLONG_MAX}, + + {L_, ULLONG_MAX, Ti( 1, 0), ULLONG_MAX}, + {L_, ULLONG_MAX, Ti( 0, 1), U_NS} + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti = 0; ti < NUM_DATA; ti++) { + const Uint64 LINE = DATA[ti].d_line; + const Uint64 RATE = DATA[ti].d_drainRate; + const Ti WINDOW = DATA[ti].d_timeWindow; + const Uint64 EXPECTED_CAPACITY = DATA[ti].d_expectedCapacity; + + LOOP_ASSERT(LINE, EXPECTED_CAPACITY == + Obj::calculateCapacity(RATE, WINDOW)); + } + + // C-4 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + ASSERT_FAIL(Obj::calculateCapacity(ULLONG_MAX, Ti(2))); + ASSERT_FAIL(Obj::calculateCapacity(1000, Ti(-1))); + } + } break; + case 15: { + // ---------------------------------------------------------------- + // 'calculateTimeToSubmit' + // Ensure that 'calculateTimeToSubmit' calculates wait interval, + // taking reserved units into account and correctly updates object + // state in contractually specified case. + // + // Concerns: + //: 1 The method calculates time till next submission correctly. + //: + //: 2 If the calculated interval is shorter than 1 nanosecond, it is + //: rounded to 1 nanosecond. + //: + //: 3 The method returns zero interval when something can be submitted + //: right now, does not update object state in this case. + //: + //: 4 If number of units in the bucket exceeds the capacity, the + //: method updates the 'lastUpdateTime' time and number of units in + //: the bucket. + //: + //: 5 The manipulator takes number of reserved units into account. + //: + //: 6 The manipulator does not affect the number of reserved + //: units. + //: + //: 7 QoI: Asserted precondition violations are detected when enabled. + // + // Plan: + //: 1 Using table-driven technique: + //: + //: 1 Define the set of values, containing the values of 'rate' and + //: 'capacity' attributes, number of units to be submitted and + //: reserved, initial lastUpdateTime, time of invoking the + //: 'calculateTimeToSubmit' manipulator, expected time interval + //: before submitting more units and expected values of + //: 'unitsInBucket' and 'lastUpdateTime' attributes after + //: 'calculateTimeToSubmit' invocation. + //: + //: 2 For each row of the table described in P-1 + //: + //: 1 Create an object having the specified parameters, using the + //: value constructor. + //: + //: 2 Submit and reserve specified number of units, using the + //: 'submit' and 'reserve' manipulators. (C-5) + //: + //: 3 Invoke the 'calculateTimeToSubmit' manipulator and verify + //: the returned time interval. (C-1..3) + //: + //: 4 Verify the value of 'lastUpdateTime' and 'unitsInBucket' + //: attributes. (C-4) + //: + //: 6 Verify the value of 'unitsReserved' attribute. (C-6) + //: + //: 5 Invoke the 'wouldOverflow' manipulator and verify, that + //: after waiting for the calculated time submitting more + //: units is allowed. Take into account that the number of + //: microseconds in the returned time interval is rounded + //: down. (C-1) + //: + //: 3 Verify that, in appropriate build modes, defensive checks are + //: triggered for invalid attribute values, but not triggered for + //: adjacent valid ones (using the 'BSLS_ASSERTTEST_*' macros). + //: (C-7) + // + // Testing: + // bsls::TimeInterval calculateTimeToSubmit(const TimeInterval&); + // ---------------------------------------------------------------- + + if (verbose) cout << + endl << "TESTING: 'calculateTimeToSubmit'" << endl + << "================================" << endl; + + const Uint64 M = 1000000; + const Uint64 G = 1000000000; + + // C-1 + + struct { + int d_line; + Uint64 d_drainRate; + Uint64 d_capacity; + Uint64 d_unitsToSubmit; + Uint64 d_unitsToReserve; + Ti d_creationTime; + Ti d_checkTime; + Ti d_expectedWait; + Uint64 d_expectedUnits; + Ti d_expectedUpdate; + } DATA[] = { + +// LINE RATE CAP SUB RSRV TCREATE TCHECK EXP_WAIT EXP_U EXP_UPD_T +// ---- ---- ---- ----- ---- ------- ------ ----------- ----- --------- + // C-3 + { L_, 1000, 1000, 0, 0, Ti( 0), Ti(0.5), Ti( 0), 0, Ti( 0)}, + { L_, 1000, 1000, 1000, 0, Ti( 0), Ti( 0), Ti( 0.001), 1000, Ti( 0)}, + { L_, 1000, 1000, 2000, 0, Ti( 0), Ti(0.5), Ti( 0.501), 1500, Ti(0.5)}, + // C-2 + { L_, 10*M, 10*M, 10*M, 0, Ti(0.5), Ti(0.5), Ti( 0, 100), 10*M, Ti(0.5)}, + { L_, 10*G, 10*G, 10*G, 0, Ti(0.5), Ti(0.5), Ti( 0, 1), 10*G, Ti(0.5)}, + // C-5, C-6 + { L_, 100, 1500, 1000, 750, Ti( 0), Ti(1.5), Ti( 1.01), 850, Ti(1.5)}, + { L_, 1000, 500, 1000, 500, Ti( 0), Ti(0.5), Ti( 0.501), 500, Ti(0.5)} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti = 0; ti < NUM_DATA; ti++) { + const Uint64 LINE = DATA[ti].d_line; + const Uint64 UNITS_TO_SUBMIT = DATA[ti].d_unitsToSubmit; + const Uint64 UNITS_TO_RESERVE = DATA[ti].d_unitsToReserve; + const Uint64 RATE = DATA[ti].d_drainRate; + const Uint64 CAPACITY = DATA[ti].d_capacity; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Ti CHECK_TIME = DATA[ti].d_checkTime; + const Ti EXPECTED_WAIT = DATA[ti].d_expectedWait; + const Uint64 EXPECTED_UNITS = DATA[ti].d_expectedUnits; + const Ti EXPECTED_UPDATE = DATA[ti].d_expectedUpdate; + + Obj x(RATE, CAPACITY, CREATION_TIME); + x.reserve(UNITS_TO_RESERVE); + x.submit(UNITS_TO_SUBMIT); + + LOOP_ASSERT(LINE, EXPECTED_WAIT == + x.calculateTimeToSubmit(CHECK_TIME)); + + LOOP_ASSERT(LINE, EXPECTED_UPDATE == x.lastUpdateTime()); + LOOP_ASSERT(LINE, EXPECTED_UNITS == x.unitsInBucket()); + LOOP_ASSERT(LINE, UNITS_TO_RESERVE == x.unitsReserved()); + + if (UNITS_TO_RESERVE < CAPACITY) { + LOOP_ASSERT(LINE, + false == + x.wouldOverflow(CHECK_TIME + EXPECTED_WAIT)); + } + } + + // C-7 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + Obj mA(1, 1, Ti(0)); + + const Ti TA = Ti(LLONG_MIN, -999999999); + const Ti TB = Ti(LLONG_MIN + 1, -999999999); + + mA.submit(10000); + + ASSERT_FAIL(mA.calculateTimeToSubmit(TA)); + ASSERT_PASS(mA.calculateTimeToSubmit(TB)); + + const Ti MAX_TI = Ti(LLONG_MAX, 999999999); + + Obj mB(1, 1, MAX_TI); + + mB.submit(10000); + + ASSERT_FAIL(mB.calculateTimeToSubmit(TA)); + ASSERT_PASS(mB.calculateTimeToSubmit(Ti(0))); + } + } break; + case 14: { + // -------------------------------------------------------------------- + // 'calculateTimeToSubmit', CLASS METHOD 'calculateDrainTime' + // Ensure that the 'calculateTimeToSubmit' manipulator calculates the + // wait interval until next submission with nanosecond precision and + // 'calculateDrainTime' class method calculates the time required to + // drain the specified number of units at the specified rate + // correctly. + // + // Concerns: + //: 1 The 'calculateTimeToSubmit' manipulator calculates the wait + //: interval according to contractually specified behavior. + //: + //: 2 The 'calculateDrainTime' class method calculates the time + //: required to drain the specified number of units at the specified + //: rate correctly. + // + // Plan: + //: 1 Using table-driven technique: + //: + //: 1 Define the set of values, containing the value of 'rate' + //: attribute, number of units, that must be drained before + //: submitting more units will be allowed and the expected time + //: interval before submitting more units. + //: + //: 2 For each row of the table described in P-1 + //: + //: 1 Create an object having the capacity of 1 units and the + //: specified drain rate, using the value constructor. + //: + //: 2 Submit units, using the 'submit' manipulator. + //: + //: 3 Invoke the 'calculateTimeToSubmit' manipulator and verify the + //: returned time interval. (C-1) + //: + //: 4 Invoke the 'calculateDrainTime' class method and verify the + //: returned time interval. (C-2) + //: + //: 5 Invoke the 'wouldOverflow' manipulator and verify, that after + //: waiting for the calculated time submitting more units is + //: allowed. Take into account that the number of microseconds in + //: the returned time interval is rounded down. + // + // Testing: + // calculateDrainTime(numOfUnits, drainRate, ceilFlag); + // bsls::TimeInterval calculateTimeToSubmit(currentTime); + // ---------------------------------------------------------------- + + if (verbose) cout << endl + << "'calculateTimeToSubmit'" << endl + << "=======================" << endl; + + const Uint64 MAX_R = ULLONG_MAX; + const Uint64 Gb = 1 << 30; + const Uint64 G = 1000000000; + const Uint64 M = 1000000; + + const Ti MNS_T(0, 999999999); + + // Numbers of units to be consumed during different intervals + // at maximum rate. + const Uint64 U_5NS = (MAX_R / G) * 5 + ((MAX_R % G) * 5) / G; + const Uint64 U_20NS = (MAX_R / G) * 20 + ((MAX_R % G) * 20) / G; + const Uint64 U_NS = MAX_R / G; + + static const struct { + int d_line; + Uint64 d_rate; + Uint64 d_units; + Ti d_expTime; + } DATA[] = { + // LINE RATE BACKLOG UNITS EXP_TIME + // ---- ---------- --------------- -------------------------- + { L_, ULLONG_MAX, ULLONG_MAX - 1, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + 1000000, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + 500000000, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + 750000000, Ti( 1, 0)}, + + { L_, ULLONG_MAX, 1, Ti( 0, 1)}, + + { L_, 100 * G + + 500 * M, ULLONG_MAX, Ti( 183549692, 275716932)}, + + { L_, 100000 * G + + 500 * M, ULLONG_MAX, Ti( 184466, 518404504)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + 1000000000, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + U_NS + U_NS/2, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + U_NS + U_NS/2 + + 1, Ti( 1, 0)}, + + { L_, ULLONG_MAX, ULLONG_MAX - + U_NS + U_NS/2 - + 1, Ti( 1, 0)}, + + { L_, ULLONG_MAX, U_NS, Ti( 0, 1)}, + { L_, ULLONG_MAX, U_5NS, Ti( 0, 5)}, + { L_, ULLONG_MAX, U_20NS, Ti( 0, 20)}, + + { L_, 150, 15, Ti( 0, 100000000)}, + + { L_, 1701, 1500, Ti( 0, 881834216)}, + + { L_, 150*G, 15*G, Ti( 0, 100000000)}, + + { L_, G+1, ULLONG_MAX, Ti(18446744055LL,262807560)}, + { L_, G-1, ULLONG_MAX, Ti(18446744092LL,156295708)}, + { L_, G, ULLONG_MAX, Ti(18446744073LL,709551615)}, + + { L_, Gb+1, ULLONG_MAX, Ti(17179869168LL, 14)}, + { L_, Gb-1, ULLONG_MAX, Ti(17179869200LL, 14)}, + { L_, Gb+1, 50*Gb+1, Ti( 49, 999999955)}, + { L_, Gb+1, 50*Gb+5, Ti( 49, 999999959)}, + { L_, Gb-1, 50*Gb+1, Ti( 50, 48)}, + { L_, Gb-1, 50*Gb+5, Ti( 50, 52)}, + { L_, Gb-42, 50*Gb+1, Ti( 50, 1957)}, + { L_, Gb+42, 50*Gb+5, Ti( 49, 999998049)} + }; + + const int NUM_DATA = sizeof DATA / sizeof *DATA; + + for (int ti = 0; ti < NUM_DATA; ++ti) { + const int LINE = DATA[ti].d_line; + const Uint64 RATE = DATA[ti].d_rate; + const Uint64 UNITS = DATA[ti].d_units; + const Ti EXP_T = DATA[ti].d_expTime; + + Obj x(RATE, 1, Ti(0)); + x.submit(UNITS); + + Ti t = x.calculateTimeToSubmit(Ti(0)); + Ti dT = balb::LeakyBucket::calculateDrainTime(UNITS, + RATE, + true); + + LOOP_ASSERT(LINE, EXP_T == dT); + LOOP_ASSERT(LINE, EXP_T == t); + LOOP_ASSERT(LINE, false == x.wouldOverflow(t)); + } + } break; + case 13: { + // ---------------------------------------------------------------- + // CLASS METHOD 'reset' + // Ensure that the 'reset' manipulator resets object to its initial + // state. + // + // Concerns: + //: 1 The values of 'rate' and 'capacity attributes' is not affected + //: by the 'reset' method. + //: + //: 2 'reset' method resets the object to its default-constructed + //: state and sets 'lastUpdateTime' correctly. + //: + //: 3 'reset' method updates the value of + //: 'statisticsCollectionStartTime' attribute and resets the + //: statistics counter. + //: + //: 4 QoI: Asserted precondition violations are detected when enabled. + // + // Plan: + //: 1 Define the values of object parameters ('rate' and 'capacity' + //: attributes) that will be used throughout the test. + //: + //: 2 Using table-driven technique: + //: + //: 1 Define the set of values, containing the time of object + //: creation, number of units to be submitted and the time + //: of resetting object state, including the boundary values + //: corresponding to every range of values that each + //: individual attribute can independently attain. + //: + //: 3 For each row of the table described in P-2 + //: + //: 1 Create an object with the specified parameters using the value + //: constructor. + //: + //: 2 Submit and reserve units, using the 'submit' and 'reserve' + //: manipulator. + //: + //: 3 Invoke the 'reset' manipulator. + //: + //: 4 Verify the object attributes that must not be affected by the + //: 'reset' manipulator. (C-1) + //: + //: 5 Verify the object attributes that are to be reset by the + //: 'reset' manipulator. (C-2..3) + //: + //: 4 Verify that, in appropriate build modes, defensive checks are + //: triggered for invalid attribute values, but not triggered for + //: adjacent valid ones (using the 'BSLS_ASSERTTEST_*' macros). + //: (C-4) + // + // Testing: + // void reset(const bsls::TimeInterval& currentTime); + // ---------------------------------------------------------------- + + if (verbose) cout << endl + << "'reset'" << endl + << "=======" << endl; + + Uint64 usedUnits = 0; + Uint64 unusedUnits = 0; + const Uint64 CAPACITY(1000); + const Uint64 RATE(1000); + + const Ti MAX_TI = Ti(LLONG_MAX, 999999999); + const Ti MIN_TI = Ti(LLONG_MIN + 1, -999999999); + + struct { + int d_line; + Ti d_creationTime; + Uint64 d_units; + Ti d_resetTime; + } DATA[] = { + // LINE CTIME UNITS TRESET + // ---- ------- ----------- ------ + { L_, Ti( 0), 0, Ti( 0) }, + { L_, Ti( 0), 1000, Ti( 0) }, + { L_, Ti( 0), 2000, Ti( 0) }, + { L_, Ti(50), 0, Ti(60) }, + { L_, Ti(50), 1000, Ti(60) }, + { L_, Ti(50), 0, Ti( 0) }, + { L_, Ti(50), 1000, Ti( 0) }, + + { L_, Ti( 0), LLONG_MAX, Ti( 0) }, + { L_, MAX_TI, 1000, Ti( 0) }, + { L_, Ti( 0), 1000, MAX_TI }, + { L_, MIN_TI, 1000, Ti( 0) }, + { L_, Ti( 0), 1000, MIN_TI }, + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti = 0; ti < NUM_DATA; ti++) { + const int LINE = DATA[ti].d_line; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Uint64 UNITS = DATA[ti].d_units; + const Ti RESET_TIME = DATA[ti].d_resetTime; + + Obj x(RATE, CAPACITY, CREATION_TIME); + x.submit(UNITS); + x.reserve(UNITS); + + x.updateState(RESET_TIME); + x.reset(RESET_TIME); + + // C-1 + LOOP_ASSERT(LINE, RATE == x.drainRate()); + LOOP_ASSERT(LINE, CAPACITY == x.capacity()); + + // C-2 + LOOP_ASSERT(LINE, 0 == x.unitsInBucket()); + LOOP_ASSERT(LINE, 0 == x.unitsReserved()); + LOOP_ASSERT(LINE, RESET_TIME == x.lastUpdateTime()); + + // C-3 + x.getStatistics(&usedUnits, &unusedUnits); + + LOOP_ASSERT(LINE, 0 == usedUnits); + LOOP_ASSERT(LINE, 0 == unusedUnits); + LOOP_ASSERT(LINE, RESET_TIME == x.statisticsCollectionStartTime()); + } + + // C-4 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + Obj mA(1, 1, Ti(0)); + + const Ti TA = Ti(LLONG_MIN, -999999999); + const Ti TB = Ti(LLONG_MIN + 1, -999999999); + + ASSERT_SAFE_FAIL(mA.reset(TA)); + ASSERT_PASS(mA.reset(TB)); + } + + } break; + case 12: { + // -------------------------------------------------------------------- + // 'resetStatistics' + // Ensure that the 'resetStatistics' manipulator resets the object + // statistics to its default-constructed state. + // + // Concerns: + //: 1 'resetStatistics' resets unit statistics counter to 0. + //: + //: 2 'resetStatistics' updates 'statisticsCollectionStartTime' time + //: correctly. + //: + //: 3 'resetStatistics' does not alter object state except for + //: submitted units counter and 'statisticsCollectionStartTime' + //: time. + // + // Plan: + //: 1 Define the object parameters. + //: + //: 2 Create an object using the value constructor. + //: + //: 3 Submit some units and verify values returned by the + //: 'getStatistics' accessor. + //: + //: 4 Invoke the 'resetStatistics' manipulator. + //: + //: 5 Invoke the 'getStatistics' accessor. Verify returned values. + //: (C-1) + //: + //: 6 Verify value of the 'statisticsCollectionStartTime' attribute. + //: (C-2) + //: + //: 7 Verify the values of other object attributes ensure, that they + //: were not affected by the 'resetStatistics' manipulator. (C-3) + // + // Testing: + // void resetStatistics(); + // -------------------------------------------------------------------- + + if (verbose) cout << + endl << "'resetStatistics'" << endl + << "=================" << endl; + + Uint64 usedUnits = 0; + Uint64 unusedUnits = 0; + const Ti CREATION_TIME = Ti(0.0); + const Ti UPD_TIME = Ti(0.75); + const Uint UNITS = 500; + const Uint64 RATE = 1000; + const Uint64 CAPACITY = 1000; + const Uint64 EXP_USED = UNITS; + + const Uint64 EXP_UNUSED = (Uint64) floor(( + UPD_TIME - CREATION_TIME).totalSecondsAsDouble()*RATE) - EXP_USED; + + Obj x(RATE, CAPACITY, CREATION_TIME); + x.submit(UNITS); + x.updateState(UPD_TIME); + + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(EXP_USED == usedUnits); + ASSERT(EXP_UNUSED == unusedUnits); + ASSERT(CREATION_TIME == x.statisticsCollectionStartTime()); + + x.submit(UNITS); + x.resetStatistics(); + + // C-1 + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(0 == usedUnits); + ASSERT(0 == unusedUnits); + + // C-2 + ASSERT(UPD_TIME == x.statisticsCollectionStartTime()); + + // C-3 + ASSERT(RATE == x.drainRate()); + ASSERT(CAPACITY == x.capacity()); + ASSERT(UPD_TIME == x.lastUpdateTime()); + ASSERT(UNITS == x.unitsInBucket()); + } break; + case 11: { + // -------------------------------------------------------------------- + // 'getStatistics' + // Ensure, that the 'getStatistics' method correctly calculate + // numbers of used and unused units. + // + // Concerns: + //: 1 'getStatistics' returns 0 for a new object, created by value + //: CTOR. + //: + //: 2 'getStatistics' returns correct numbers of used and unused units + //: after a sequence of 'submit' and 'updateState' calls. + //: + //: 3 Specifying invalid parameters for 'getStatistics' causes certain + //: behavior in special build configuration. + //: + //: 4 Statistics is calculated for interval between + //: 'statisticsCollectionStartTime' and 'lastUpdateTime'. + //: + //: 5 Statistics is calculated correctly, if time specified to + //: 'updateState' precedes 'statisticsCollectionStartTime'. + // + // Plan: + //: 1 Construct the object using the value constructor and verify the + //: values returned by the 'getStatistics' method. + //: + //: 2 Using table-driven technique: + //: + //: 1 Define the set of values, containing the 'rate' parameter, + //: number of units to submit at each iteration, interval between + //: 'updateState' invocations, number of 'submit' and + //: 'updateState' invocations and expected numbers of used and + //: unused units after the foregoing operations. + //: + //: 3 For each row of the table described in P-2 + //: + //: 1 Create an object with the specified parameters. + //: + //: 2 Execute the inner loop, invoking 'submit' and 'updateState' + //: methods the specified number of times. + //: + //: 3 Invoke the 'getStatistics' method and verify the returned + //: numbers of used and unused units. + //: + //: 4 Create an object, submit some units, invoke the 'updateState' + //: manipulator several times and verify the values returned by the + //: 'getStatistics' method between the 'updateState' invocations. + //: + //: 5 Create an object specifying lastUpdateTime 'T1', submit some + //: units, invoke the 'updateState' manipulator specifying + //: lastUpdateTime 'T2', that is before 'T1' and verify the values + //: returned by 'getStatistics'. Invoke 'updateState' again, + //: specifying lastUpdateTime 'T3', that is after 'T2', verify the + //: values, returned by 'getStatistics'. + //: + //: 6 Verify that, in appropriate build modes, defensive checks + //: are triggered for invalid parameters. + // + // Testing: + // void getStatistics(smtUnits, unusedUnits) const; + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "'getStatistics'" << endl + << "===============" << endl; + + Uint64 usedUnits = 0; + Uint64 unusedUnits = 0; + + + const Uint64 MAX_R = ULLONG_MAX; + const Uint64 G = 1000000000; + + const Ti MNS_T(0, 999999999); + + // Numbers of units to be consumed during different intervals + // at maximum rate. + + const Uint64 U_1S = ULLONG_MAX; + const Uint64 U_5NS = (MAX_R / G) * 5 + ((MAX_R % G) * 5) / G; + const Uint64 U_20NS = (MAX_R / G) * 20 + ((MAX_R % G) * 20) / G; + const Uint64 U_MNS = (MAX_R / G) * 999999999 + + ((MAX_R % G) * 999999999) / G; + const Uint64 U_NS = MAX_R / G; + + // C-1 + if (verbose) cout + << endl + << "Testing: statistics after construction " + "using value ctor" + << endl; + { + Obj x(1000, 1000, Ti(42.4242)); + + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(0 == usedUnits); + ASSERT(0 == unusedUnits); + } + + // C-2 + if (verbose) cout << endl + << "Testing: statistics calculation" + << endl; + { + const Uint64 CAPACITY = 1000; + + struct { + int d_line; + Uint64 d_drainRate; + Uint64 d_units; + Ti d_creationTime; + Ti d_updateInterval; + int d_NumOfUpdates; + Uint64 d_expectedUsed; + Uint64 d_expectedUnused; + } DATA[] = { + + // LINE RATE UNITS TCREATE UPDATE_INT N_UPD USED_UNITS UNUSED_UNITS + // ---- ----- ------- ------- ---------- ----- ---------- ------------ + {L_, 1000, 1000, Ti( 0), Ti( 0.01), 10, 10000, 0}, + {L_, 1000, 100, Ti( 0), Ti( 0.5), 5, 500, 2000}, + {L_, 100, 0, Ti(10), Ti( 0.1), 20, 0, 200}, + {L_, 1000, 500, Ti( 0), Ti( 0), 5, 2500, 0}, + // Testing operation at high speed + {L_, MAX_R, 0, Ti( 0), Ti( 0, 1), 5, 0, U_5NS}, + {L_, MAX_R, 0, Ti( 0), MNS_T, 1, 0, U_MNS}, + {L_, MAX_R, 1000, Ti( 0), Ti( 0, 5), 4, 4000, U_20NS - + 4000}, + {L_, MAX_R, U_1S, Ti( 0), Ti( 1), 1, U_1S, 0}, + {L_, MAX_R, U_NS, Ti( 0), Ti( 1), 1, U_NS, U_1S - U_NS} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + for (int ti = 0; ti < NUM_DATA; ++ti) { + const int LINE = DATA[ti].d_line; + const Uint64 RATE = DATA[ti].d_drainRate; + const Uint64 UNITS = DATA[ti].d_units; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Ti UPDATE_INTERVAL = DATA[ti].d_updateInterval; + const int NUM_OF_UPDATES = DATA[ti].d_NumOfUpdates; + const Uint64 EXPECTED_USED = DATA[ti].d_expectedUsed; + const Uint64 EXPECTED_UNUSED = DATA[ti].d_expectedUnused; + + Obj x(RATE, CAPACITY, CREATION_TIME); + Ti currentTime(CREATION_TIME); + + for(int i = 0; i < NUM_OF_UPDATES; ++i) { + currentTime += UPDATE_INTERVAL; + x.submit(UNITS); + x.updateState(currentTime); + } + + x.getStatistics(&usedUnits, &unusedUnits); + LOOP_ASSERT(LINE, EXPECTED_USED == usedUnits); + LOOP_ASSERT(LINE, EXPECTED_UNUSED == unusedUnits); + + } + } + + // C-3 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + Obj x(1, 1, Ti(0)); + + ASSERT_SAFE_FAIL(x.getStatistics(0,&unusedUnits)); + ASSERT_SAFE_FAIL(x.getStatistics(&usedUnits,0)); + ASSERT_SAFE_FAIL(x.getStatistics(0,0)); + } + + // C-4 + if (verbose) cout << endl + << "Testing statistics collection interval" + << endl; + { + Obj x(1000, 1000, Ti(0)); + x.submit(1000); + + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(0 == usedUnits); + ASSERT(0 == unusedUnits); + + x.updateState(Ti(0.1)); + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(1000 == usedUnits); + ASSERT(0 == unusedUnits); + + x.updateState(Ti(10)); + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(1000 == usedUnits); + ASSERT(9000 == unusedUnits); + + x.resetStatistics(); + x.updateState(Ti(15)); + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(0 == usedUnits); + ASSERT(5000 == unusedUnits); + } + + // C-5 + if (verbose) cout << endl + << "Testing statistics collection, " + "time goes backwards" + << endl; + { + Obj x(1000, 1000, Ti(10)); + + x.submit(500); + x.updateState(Ti(5)); + + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(500 == usedUnits); + ASSERT(0 == unusedUnits); + + x.updateState(Ti(8)); + + x.getStatistics(&usedUnits, &unusedUnits); + ASSERT(500 == usedUnits); + ASSERT(2500 == unusedUnits); + } + } break; + case 10: { + // ---------------------------------------------------------------- + // 'cancelReserved', 'submitReserved' + // Ensure that 'cancelReserved', 'submitReserved' manipulators + // correctly update 'unitsReserved' and 'unitsInBucket' attributes. + // + // Concerns: + //: 1 'cancelReserved' decrements 'unitsReserved', without + //: affecting any other attributes. + //: + //: 2 'submitReserved' decrements 'unitsReserved' and increments + //: 'unitsInBucket' + //: + //: 3 'submitReserved' submits units disregarding state of object. + //: + //: 4 QoI: Asserted precondition violations in the + //: 'submitReserved' manipulator are detected when enabled. + // + // Plan: + //: 1 Define the object parameters, that will be used throughout + //: the test. The 'rate' and 'capacity' parameters do not affect + //: the behavior of 'submitReserved' and 'cancelReserved' + //: manipulators, so they are used for the whole test set. + //: + //: 2 Using the table-driven technique: + //: + //: 1 Define the set of values, containing number of units to be + //: reserved and the numbers of units to be submitted and + //: canceled from the reservation. + //: + //: 3 For each row in the table, defined in P-2: + //: + //: 1 Create an object having the specified parameters. + //: + //: 2 Reserve the specified number of units, + //: + //: 3 Invoke the 'submitReserved' and 'cancelReserved' + //: manipulators with the specified numbers of units to be + //: submitted and canceled. + //: + //: 4 Verify values of the 'unitsReserved' and 'unitsSubmitted' + //: attributes. (C-1..3) + //: + //: 4 Verify that, in appropriate build modes, defensive checks + //: are triggered for invalid attribute values, but not + //: triggered for adjacent valid ones (using the + //: 'BSLS_ASSERTTEST_*' macros). (C-4) + // + // Testing: + // void submitReserved(bsls::Types::Unit64 numOfUnits); + // void cancelReserved(bsls::Types::Unit64 numOfUnits); + // ---------------------------------------------------------------- + + if (verbose) cout << endl + << "'submitReserved', 'cancelReserved'" << endl + << "==================================" << endl; + + const Uint64 RATE = 1000; + const Uint64 CAPACITY = 1000; + const Uint64 MAX_U = ULLONG_MAX; + + struct { + int d_line; + Uint64 d_unitsToReserve; + Uint64 d_unitsToSubmit; + Uint64 d_unitsToCancel; + Uint64 d_expectedUnitsReserved; + Uint64 d_expectedUnitsInBucket; + } DATA[] = { + + // LINE RESERVE SUBMIT CANCEL EXP_RES EXP_SUB + // ---- ------- ------- ------- -------- --------- + + {L_, 1000, 300, 0, 700, 300}, + {L_, 1000, 500, 500, 0, 500}, + + {L_, MAX_U, 0, 0, MAX_U, 0}, + {L_, MAX_U, MAX_U, 0, 0, MAX_U}, + {L_, MAX_U, MAX_U/2, MAX_U/2, 1, MAX_U/2}, + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti=0; ti(x, + RATE, + CAPACITY, + DATA_SIZE, + CHUNK_SIZE, + Ti(0)); + + LOOP_ASSERT(LINE, DATA_SIZE == x.unitsInBucket()); + LOOP_ASSERT(LINE, RATE == x.rate()); + LOOP_ASSERT(LINE, CAPACITY == x.capacity()); + LOOP_ASSERT(LINE, EXP_DUR == x.lastUpdateTime()); + + ASSERT(EXP_DUR == dur); + } + } + + } break; + case 1: { + // -------------------------------------------------------------------- + // BREATHING TEST: + // Developers' Sandbox. + // + // Concerns: + //: 1 The class is sufficiently functional to enable comprehensive + //: testing in subsequent cases + // + // Plan: + //: 1 Create an object, using the value ctor. + //: + //: 2 Invoke the 'setRateAndCapacity' manipulator. + //: + //: 3 Invoke the 'rate' and 'capacity' accessors and check the + //: returned values. + //: + //: 4 Invoke the 'submit' and 'reserve' manipulators. + //: + //: 5 Invoke the 'unitsInBucket' and 'unitsReserved' accessors + //: and check the returned value. + //: + //: 6 Invoke the 'wouldOverflow' and 'calculateTimeToSubmit' + //: manipulators. + //: + //: 7 Invoke the 'updateState' manipulator. + // + // Testing: + // BREATHING TEST + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "BREATHING TEST" << endl + << "==============" << endl; + + Obj x(1, 1, Ti(0)); + Ti currentTime(1); + ASSERT(1 == x.drainRate()); + ASSERT(1 == x.capacity()); + ASSERT(0 == x.lastUpdateTime()); + + x.setRateAndCapacity(1000, 1000); + x.reset(currentTime); + + ASSERT(1000 == x.drainRate()); + ASSERT(1000 == x.capacity()); + + x.submit(500); + x.reserve(250); + ASSERT(500 == x.unitsInBucket()); + ASSERT(250 == x.unitsReserved()); + + ASSERT(false == x.wouldOverflow(currentTime)); + ASSERT(Ti(0) == x.calculateTimeToSubmit(currentTime)); + x.submitReserved(250); + x.submit(750); + + ASSERT(0 == x.unitsReserved()); + ASSERT(1500 == x.unitsInBucket()); + + ASSERT(Ti(0, 501000000) == x.calculateTimeToSubmit(currentTime)); + + currentTime.addMilliseconds(500); + ASSERT(true == x.wouldOverflow(currentTime)); + + currentTime.addMilliseconds(100); + x.updateState(currentTime); + ASSERT(900 == x.unitsInBucket()); + + x.submit(100); + ASSERT(1000 == x.unitsInBucket()); + } break; + default: { + cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; + testStatus = -1; + } + } + + if (testStatus > 0) { + cerr << "Error, non-zero test status = " << testStatus << "." << endl; + } + return testStatus; +} + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_performancemonitor.cpp b/groups/bal/balb/balb_performancemonitor.cpp index 6e7e27f202..d3ad8ae746 100644 --- a/groups/bal/balb/balb_performancemonitor.cpp +++ b/groups/bal/balb/balb_performancemonitor.cpp @@ -15,34 +15,37 @@ BSLS_IDENT_RCSID(balb_performancemonitor_cpp,"$Id$ $CSID$") #include #include -#include +#include #include #include #include +#include + +#include +#include + +#include #include +#include +#include +#include +#include #include #include #include #include #include +#include #include #include +#include #include #include #include -#include -#include - -#include -#include -#include -#include -#include - #if defined(BSLS_PLATFORM_OS_UNIX) #include #include @@ -150,6 +153,185 @@ namespace balb { #if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_CYGWIN) + // -------------------------------------- + // PerformanceMonitor_LinuxProcStatistics + // -------------------------------------- + +// CLASS METHOD +int PerformanceMonitor_LinuxProcStatistics::readProcStatString( + bsl::string *buffer, + int pid) +{ + bsl::ostringstream oss; + oss << "/proc/" << pid << "/stat"; + const bsl::string& ossString = oss.str(); + const char * const filename = ossString.c_str(); + + bsl::ifstream ifs(filename); + if (!ifs) { + BSLS_LOG_DEBUG("Failed to open '%s'", filename); + return -1; // RETURN + } + + BSLS_TRY { + // The following has been observed to throw with some gcc versions + // (reportedly 4.1.2 and 4.8.1). See internal ticket 57176174. + + buffer->clear(); + bsl::getline(ifs, *buffer); + + return 0; // RETURN + } + BSLS_CATCH (...) { + } + + BSLS_LOG_DEBUG("'getline' threw"); + + return -1; +} + +// CREATOR +PerformanceMonitor_LinuxProcStatistics:: +PerformanceMonitor_LinuxProcStatistics() +: d_pid() +, d_comm() +, d_state() +, d_ppid() +, d_pgrp() +, d_session() +, d_tty_nr() +, d_tpgid() +, d_flags() +, d_minflt() +, d_cminflt() +, d_majflt() +, d_cmajflt() +, d_utime() +, d_stime() +, d_cutime() +, d_cstime() +, d_priority() +, d_nice() +, d_numThreads() +, d_itrealvalue() +, d_starttime() +, d_vsize() +, d_rss() +{ +} + +// MANIPULATOR +int PerformanceMonitor_LinuxProcStatistics::parseProcStatString( + const bsl::string& procStatString, + int pid) +{ + *this = PerformanceMonitor_LinuxProcStatistics(); + + const char *psCStr = procStatString.c_str(); + + bsl::size_t commIdx = procStatString.find('('); + if (bsl::string::npos == commIdx) { + BSLS_LOG_DEBUG("Error parsing '(' of '%s'", psCStr); + return -1; // RETURN + } + + // There are 22 fields following 'comm', the first of which is '%c' + // guaranteed not to contain parens, and the rest of which are integral. + // All of these fields are at least 1 char wide (in practice most of them + // are just 0), plus spaces is a minimum width of those fields of 44. More + // fields may be appeneded to the 'stat' file in future versions of Linux, + // and they may be '%s' or '%c' fields containing parens, so we want to be + // sure not to search over them. + + // The 'comm' field, according to current documentation, is up to 16 chars + // long, and enclosed in parens, but will often be shorter. The shortest + // 'comm' field would be the empty string contained in parens. It is + // conceivable but very unlikely that the length limitation on the 'comm' + // field may grow a bit. So let's search backward for the terminating ')' + // starting at 'commIdx + 2 + 40'. + + if (procStatString.length() < commIdx + 2 + 40) { + // Failure: the string should always be at least this long. + + BSLS_LOG_DEBUG("Incomplete input '%s'", psCStr); + return -1; // RETURN + } + + const bsl::size_t lastParen = procStatString.rfind(')', commIdx + 2 + 40); + if (bsl::string::npos == lastParen || lastParen <= commIdx) { + BSLS_LOG_DEBUG("Error parsing ')' of '%s'", psCStr); + return -1; // RETURN + } + + d_comm = procStatString.substr(commIdx, lastParen + 1 - commIdx); + + const bsl::size_t stateIdx = procStatString.find_first_not_of( + " ", + lastParen + 1); + if (bsl::string::npos == stateIdx) { + BSLS_LOG_DEBUG("Error parsing 'state' of '%s'", psCStr); + return -1; // RETURN + } + + BSLS_TRY { + // The following has been observed to throw with some gcc versions + // (reportedly 4.1.2 and 4.8.1). See internal ticket 57176174. + + bdlsb::FixedMemInStreamBuf sb(psCStr, + procStatString.length()); + bsl::istream ibs(&sb); + + ibs >> d_pid; + if (pid != d_pid) { + BSLS_LOG_DEBUG("Error parsing pid of '%s'", psCStr); + return -1; // RETURN + } + + ibs.seekg(stateIdx); + ibs >> d_state; + + if (!ibs) { + BSLS_LOG_DEBUG("Error parsing state field of '%s'", psCStr); + return -1; // RETURN + } + + ibs >> d_ppid + >> d_pgrp + >> d_session + >> d_tty_nr + >> d_tpgid + >> d_flags + >> d_minflt + >> d_cminflt + >> d_majflt + >> d_cmajflt + >> d_utime + >> d_stime + >> d_cutime + >> d_cstime + >> d_priority + >> d_nice + >> d_numThreads + >> d_itrealvalue + >> d_starttime + >> d_vsize + >> d_rss; + + if (!ibs) { + BSLS_LOG_DEBUG("Error parsing integral fields of '%s'", psCStr); + return -1; // RETURN + } + + return 0; // RETURN + } + BSLS_CATCH (...) { + } + + BSLS_LOG_DEBUG("Throw when parsing '%s'", psCStr); + + return -1; +} + template <> class PerformanceMonitor::Collector { // Provide a specialization of the 'Collector' class template for the Linux @@ -163,102 +345,32 @@ class PerformanceMonitor::Collector { // 'Collector' template requires a constructor accepting a single // 'bslma::Allocator' argument. - // PRIVATE TYPES - - struct ProcStatistics { - // Describes the fields present in /proc//stat. For a complete - // description of each field, see 'man proc'. - // - // Note that sizes of the data fields are defined in terms of scanf(3) - // format specifiers, such as %d, %lu or %c. There is no good way to - // know if %lu is 32-bit wide or 64-bit, because the code can be built - // in the -m32 mode making sizeof(unsigned long)==4 and executed on a - // 64bit platform where the kernel thinks that %lu can represent 64-bit - // wide integers. Therefore we use 'Uint64' regardless of the build - // configuration. - - typedef bsls::Types::Int64 LdType; - typedef bsls::Types::Uint64 LuType; - typedef bsls::Types::Uint64 LluType; - - int d_pid; // process pid - bsl::string d_comm; // filename of executable - char d_state; // process state - int d_ppid; // process's parent pid - int d_pgrp; // process group id - int d_session; // process session id - int d_tty_nr; // the tty used by the process - int d_tpgid; // tty owner's group id - unsigned int d_flags; // kernel flags - LuType d_minflt; // num minor page faults - LuType d_cminflt; // num minor page faults - children - LuType d_majflt; // num major page faults - LuType d_cmajflt; // num major page faults - children - LuType d_utime; // num jiffies in user mode - LuType d_stime; // num jiffies in kernel mode - LdType d_cutime; // num jiffies, user mode, children - LdType d_cstime; // num jiffies, kernel mode, children - LdType d_priority; // standard nice value, plus fifteen - LdType d_nice; // nice value - LdType d_numThreads; // number of threads (since Linux 2.6) - LdType d_itrealvalue; // num jiffies before next SIGALRM - LluType d_starttime; // time in jiffies since system boot - LuType d_vsize; // virtual memory size, in bytes - LdType d_rss; // resident set size, in pages + // TYPES + typedef PerformanceMonitor_LinuxProcStatistics ProcStatistics; - ProcStatistics() - : d_pid() - , d_comm() - , d_state() - , d_ppid() - , d_pgrp() - , d_session() - , d_tty_nr() - , d_tpgid() - , d_flags() - , d_minflt() - , d_cminflt() - , d_majflt() - , d_cmajflt() - , d_utime() - , d_stime() - , d_cutime() - , d_cstime() - , d_priority() - , d_nice() - , d_numThreads() - , d_itrealvalue() - , d_starttime() - , d_vsize() - , d_rss() - { - } - - // Note that subsequent fields present in '/proc//stat' are not - // required for any collected measures. - }; + private: + // NOT IMPLEMENTED + Collector(const Collector &); // = deleted + Collector& operator=(const Collector &); // = deleted - int readProcStat(ProcStatistics *stats, int pid); + // PRIVATE CLASS METHODS + static int readProcStat(ProcStatistics *stats, int pid); // Load into the specified 'stats' result the fields present for the // specified 'pid' in the '/proc//stat' virtual file. Return 0 on // success or a non-zero value otherwise. - // UNIMPLEMENTED - Collector(const Collector &); // = deleted - Collector& operator=(const Collector &); // = deleted - public: // CREATORS + explicit Collector(bslma::Allocator *basicAllocator = 0); // Create an instance of this class. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is 0, // the currently installed default allocator is used. - // METHODS - + // MANIPULATORS int initialize(PerformanceMonitor::Statistics *stats, int pid, - const bsl::string& description); + const bsl::string_view& description); // Initialize the specified 'stats' to represent the specified 'pid' // having the specified user-defined 'description'. Return 0 on // success or a non-zero value otherwise. @@ -272,45 +384,18 @@ class PerformanceMonitor::Collector { int PerformanceMonitor::Collector ::readProcStat(ProcStatistics *stats, int pid) { - bsl::stringstream filename; - filename << "/proc/" << pid << "/stat"; - - bsl::ifstream ifs(filename.str().c_str()); - if (!ifs) { - BSLS_LOG_DEBUG("Failed to open '%s'", filename.str().c_str()); - return -1; // RETURN + int rc; + bsl::string procStatString; + rc = ProcStatistics::readProcStatString(&procStatString, pid); + if (0 != rc) { + BSLS_LOG_DEBUG("readProcStat: readProcStatString failed"); + return rc; // RETURN } - BSLS_TRY { - // The following has been observed to throw with some gcc versions - // (reportedly 4.1.2 and 4.8.1). See internal ticket 57176174. - - ifs >> stats->d_pid - >> stats->d_comm - >> stats->d_state - >> stats->d_ppid - >> stats->d_pgrp - >> stats->d_session - >> stats->d_tty_nr - >> stats->d_tpgid - >> stats->d_flags - >> stats->d_minflt - >> stats->d_cminflt - >> stats->d_majflt - >> stats->d_cmajflt - >> stats->d_utime - >> stats->d_stime - >> stats->d_cutime - >> stats->d_cstime - >> stats->d_priority - >> stats->d_nice - >> stats->d_numThreads - >> stats->d_itrealvalue - >> stats->d_starttime - >> stats->d_vsize - >> stats->d_rss; - } - BSLS_CATCH (...) { + rc = stats->parseProcStatString(procStatString, pid); + if (0 != rc) { + BSLS_LOG_DEBUG("readProcStat: parseProcStatString failed"); + return rc; // RETURN } return 0; @@ -322,7 +407,7 @@ ::Collector(bslma::Allocator *) } int PerformanceMonitor::Collector -::initialize(Statistics *stats, int pid, const bsl::string& description) +::initialize(Statistics *stats, int pid, const bsl::string_view& description) { int rc; @@ -561,7 +646,9 @@ class PerformanceMonitor::Collector { // METHODS - int initialize(Statistics *stats, int pid, const bsl::string &description); + int initialize(Statistics *stats, + int pid, + const bsl::string_view& description); // Initialize the specified 'stats' to represent the specified 'pid' // having the specified user-defined 'description'. Return 0 on // success or a non-zero value otherwise. @@ -615,7 +702,7 @@ ::Collector(bslma::Allocator *) } int PerformanceMonitor::Collector -::initialize(Statistics *stats, int pid, const bsl::string& description) +::initialize(Statistics *stats, int pid, const bsl::string_view& description) { int rc; @@ -737,7 +824,9 @@ class PerformanceMonitor::Collector { // the currently installed default allocator is used. // MANIPULATORS - int initialize(Statistics *stats, int pid, const bsl::string &description); + int initialize(Statistics *stats, + int pid, + const bsl::string_view& description); // Initialize the specified 'stats' to represent the specified 'pid' // having the specified user-defined 'description'. Return 0 on // success or a non-zero value otherwise. @@ -756,7 +845,7 @@ ::Collector(bslma::Allocator *) // MANIPULATORS int PerformanceMonitor::Collector -::initialize(Statistics *stats, int pid, const bsl::string &description) +::initialize(Statistics *stats, int pid, const bsl::string_view &description) { stats->d_pid = pid; stats->d_description = description; @@ -882,7 +971,9 @@ class PerformanceMonitor::Collector { // METHODS - int initialize(Statistics *stats, int pid, const bsl::string &description); + int initialize(Statistics *stats, + int pid, + const bsl::string_view& description); // Initialize the specified 'stats' to represent the specified 'pid' // having the specified user-defined 'description'. Return 0 on // success or a non-zero value otherwise. @@ -899,7 +990,7 @@ ::Collector(bslma::Allocator *) } int PerformanceMonitor::Collector -::initialize(Statistics *stats, int pid, const bsl::string &description) +::initialize(Statistics *stats, int pid, const bsl::string_view& description) { stats->d_pid = pid; stats->d_description = description; @@ -1187,7 +1278,9 @@ struct PerformanceMonitor::Collector { // METHODS - int initialize(Statistics *stats, int pid, const bsl::string &description); + int initialize(Statistics *stats, + int pid, + const bsl::string_view& description); // Initialize the specified 'stats' to represent the specified 'pid' // having the specified user-defined 'description'. Return 0 on // success or a non-zero value otherwise. @@ -1373,7 +1466,7 @@ PerformanceMonitor::Collector::~Collector() } int PerformanceMonitor::Collector -::initialize(Statistics *stats, int pid, const bsl::string &description) +::initialize(Statistics *stats, int pid, const bsl::string_view& description) { stats->d_pid = pid; stats->d_description = description; @@ -1700,7 +1793,7 @@ PerformanceMonitor::~PerformanceMonitor() // MANIPULATORS -int PerformanceMonitor::registerPid(int pid, const bsl::string &description) +int PerformanceMonitor::registerPid(int pid, const bsl::string& description) { if (pid == 0) { pid = currentProcessPid(); diff --git a/groups/bal/balb/balb_performancemonitor.h b/groups/bal/balb/balb_performancemonitor.h index b194f817c9..685df43b8a 100644 --- a/groups/bal/balb/balb_performancemonitor.h +++ b/groups/bal/balb/balb_performancemonitor.h @@ -183,6 +183,7 @@ BSLS_IDENT("$Id: $") #include #include +#include #include #include @@ -224,10 +225,8 @@ class PerformanceMonitor { typedef bsls::Platform::OsFreeBsd OsType; #elif defined(BSLS_PLATFORM_OS_DARWIN) typedef bsls::Platform::OsDarwin OsType; -/* -#elif defined(BSLS_PLATFORM_OS_HPUX) -typedef bsls::Platform::OsHpUx OsType; -*/ +// #elif defined(BSLS_PLATFORM_OS_HPUX) +// typedef bsls::Platform::OsHpUx OsType; #elif defined(BSLS_PLATFORM_OS_UNIX) typedef bsls::Platform::OsUnix OsType; #elif defined(BSLS_PLATFORM_OS_WINDOWS) @@ -605,6 +604,86 @@ typedef bsls::Platform::OsHpUx OsType; // Return the number of processes registered for statistics collection. }; +#if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_CYGWIN) + +struct PerformanceMonitor_LinuxProcStatistics { + // Describes the fields present in /proc//stat. For a complete + // description of each field, see 'man proc'. + // + // Note that sizes of the data fields are defined in terms of scanf(3) + // format specifiers, such as %d, %lu or %c. There is no good way to know + // if %lu is 32-bit wide or 64-bit, because the code can be built in the + // -m32 mode making sizeof(unsigned long)==4 and executed on a 64bit + // platform where the kernel thinks that %lu can represent 64-bit wide + // integers. Therefore we use 'Uint64' regardless of the build + // configuration. + + // PUBLIC TYPES + typedef bsls::Types::Int64 LdType; + typedef bsls::Types::Uint64 LuType; + typedef bsls::Types::Uint64 LluType; + + // PUBLIC DATA + int d_pid; // process pid + bsl::string d_comm; // filename of executable + char d_state; // process state + int d_ppid; // process's parent pid + int d_pgrp; // process group id + int d_session; // process session id + int d_tty_nr; // the tty used by the process + int d_tpgid; // tty owner's group id + unsigned int d_flags; // kernel flags + LuType d_minflt; // num minor page faults + LuType d_cminflt; // num minor page faults - children + LuType d_majflt; // num major page faults + LuType d_cmajflt; // num major page faults - children + LuType d_utime; // num jiffies in user mode + LuType d_stime; // num jiffies in kernel mode + LdType d_cutime; // num jiffies, user mode, children + LdType d_cstime; // num jiffies, kernel mode, children + LdType d_priority; // standard nice value, plus fifteen + LdType d_nice; // nice value + LdType d_numThreads; // number of threads (since Linux 2.6) + LdType d_itrealvalue; // num jiffies before next SIGALRM + LluType d_starttime; // time in jiffies since system boot + LuType d_vsize; // virtual memory size, in bytes + LdType d_rss; // resident set size, in pages + + // Note that subsequent fields present in '/proc//stat' are not + // required for any collected measures. + + // CLASS METHOD + static int readProcStatString(bsl::string *buffer, int pid); + // For the specified process id 'pid', load the contents of the file + // '/proc//stat' into the specified 'buffer'. Return 0 on success + // and a non-zero value otherwise. + + private: + // NOT IMPLEMENTED + + public: + // CREATORS + PerformanceMonitor_LinuxProcStatistics(); + // Default construct all fields in this object. + + // PerformanceMonitor_LinuxProcStatistics( + // const PerformanceMonitor_LinuxProcStatistics&) = default; + + // MANIPULATORS + // PerformanceMonitor_LinuxProcStatistics& operator=( + // const PerformanceMonitor_LinuxProcStatistics& rhs) = default; + // Copy all fields of the specified 'rhs' to this object, and return a + // reference to it. + + int parseProcStatString(const bsl::string& procStatString, int pid); + // Parse the specified 'procStatString' and populate all the fields in + // this 'struct'. Check that the specified 'pid' matches the 'pid' + // field in the string. Return 0 on success and a non-zero value + // otherwise. +}; + +#endif + // ============================================================================ // INLINE DEFINITIONS // ============================================================================ diff --git a/groups/bal/balb/balb_performancemonitor.t.cpp b/groups/bal/balb/balb_performancemonitor.t.cpp index d858c0c098..c13a657f4b 100644 --- a/groups/bal/balb/balb_performancemonitor.t.cpp +++ b/groups/bal/balb/balb_performancemonitor.t.cpp @@ -14,16 +14,9 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include @@ -34,12 +27,21 @@ #include -#include -#include - +#include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #if defined(BSLS_PLATFORM_OS_UNIX) #include #include @@ -55,7 +57,10 @@ #endif using namespace BloombergLP; -using namespace bsl; +using bsl::cout; +using bsl::cerr; +using bsl::endl; +using bsl::flush; // ============================================================================ // TEST PLAN @@ -364,17 +369,21 @@ class MmapAllocator : public bslma::Allocator { MapType d_map; bslma::Allocator *d_allocator_p; - // UNIMPLEMENTED + private: + // NOT IMPLEMENTED MmapAllocator(const MmapAllocator &); // = deleted MmapAllocator& operator=(const MmapAllocator &); // = deleted public: + // CREATOR + explicit MmapAllocator(bslma::Allocator *basicAllocator = 0) : d_map(basicAllocator) , d_allocator_p(bslma::Default::allocator(basicAllocator)) { } + // MANIPULATORS virtual void *allocate(size_type size) { #if defined(BSLS_PLATFORM_OS_SOLARIS) @@ -534,15 +543,14 @@ double wasteCpuTime() #endif } -long long controlledCpuBurn() +bsls::Types::Int64 controlledCpuBurn() { - volatile long long factorial = 1; + volatile bsls::Types::Int64 factorial = 1; bsls::TimeInterval begin = bdlt::CurrentTime::now(); while (1) { - - for (int i = 1; i <= 20; ++i) { - factorial *= i; + for (int x = 1; x <= 20; ++x) { + factorial *= x; } if ((bdlt::CurrentTime::now() - begin).totalSecondsAsDouble() > 1.0) { @@ -566,6 +574,64 @@ ObjIterator advanceIt(const ObjIterator& begin, int n) } // close unnamed namespace +#if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_CYGWIN) + +namespace BetterParsingTest { + +typedef balb::PerformanceMonitor_LinuxProcStatistics ProcStats; + +void testParseProcStatStr(const bsl::string& procStatStr, + int pid, + const char *expComm = 0, + bool expRcZero = true) + // Test parsing the specified 'procStatStr', expected to have a process id + // matching the specified 'pid', expected to have a 'comm' field matching + // the optionally specified 'expComm', and where the return value of the + // parse match 0 or not depending upon the optionally specified + // 'expRcZero'. +{ + static const long clockTicksPerSec = sysconf(_SC_CLK_TCK); + static const long pageSize = sysconf(_SC_PAGESIZE); + + ProcStats procStats; + int rc = procStats.parseProcStatString(procStatStr, pid); + ASSERTV(expRcZero, rc, expRcZero == !rc); + + if (!expRcZero) { + return; // RETURN + } + + if (expComm) { + ASSERT(expComm == procStats.d_comm); + } + + const double cpuTimeU = static_cast(procStats.d_utime) / + static_cast(clockTicksPerSec); + const double cpuTimeS = static_cast(procStats.d_stime) / + static_cast(clockTicksPerSec); + + ASSERTV(cpuTimeU, 0.1 < cpuTimeU); + ASSERTV(cpuTimeU, cpuTimeU < 3); + + ASSERTV(cpuTimeS, cpuTimeS < 1); + + const double resSize = static_cast(procStats.d_rss) + * static_cast(pageSize) + / (1024 * 1024); + + ASSERT(0.1 < resSize); + + const double virtSize = + static_cast(procStats.d_vsize) / (1024 * 1024); + + ASSERTV(virtSize, 0.1 < virtSize); + ASSERTV(resSize, virtSize, resSize <= virtSize); +} + +} // close namespace BetterParsingTest + +#endif + // ============================================================================ // MAIN PROGRAM // ---------------------------------------------------------------------------- @@ -578,8 +644,10 @@ int main(int argc, char *argv[]) veryVeryVerbose = (argc > 4); veryVeryVeryVerbose = (argc > 5); + cout << "TEST " << __FILE__ << " CASE " << test << endl; + switch (test) { case 0: // Zero is always the leading case. - case 11: { + case 12: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. @@ -656,6 +724,116 @@ int main(int argc, char *argv[]) scheduler.stop(); //.. + } break; + case 11: { + // -------------------------------------------------------------------- + // TESTING COMM CONTAINING SPACES + // + // The 'comm' field normally reflects the filename of the executable, + // but it can be changed by the client to anything up to 16 bytes long. + // One way is for a child process to set a thread name in the current + // thread (this doesn't work for a non-child process). It is + // conceivable but highly unlikely that the 'comm' field may be over 16 + // bytes in the future. According to 'man proc', the 'comm' field was + // parsable with '%s' (meaning that it could not contain spaces) but + // this turned out not to be the case -- a client was configuring the + // the 'comm' field to contain spaces, which was interfering with the + // the correct functioning of this component. + // + // Concern: + //: 1 Spaces occurring in the 'comm' field on Linux caused the parsing + //: of '/proc//stat' to fail. Ensure that our parsing can + //: cope with this. + //: + //: 2 Future versions of Linux may have more fields appended to the + //: '/proc//stat' file, and we want to ensure that our parser + //: will be able to cope. + // + // Plan: + //: 1 Call 'ProcStat::readProcStatString' to read the + //: '/proc//stat' file into a string. + //: + //: 2 Parse that string with 'ProcStat::parseProcStatString' and + //: observe that the results are reasonable. + //: + //: 3 Separate out the 'front' and 'back' seconds of the string before + //: and after the 'comm' field. + //: + //: 4 Initialize an array of various strings. + //: + //: 5 Nest two loops, each iterating through the array of strings. + //: Have the outer loop dictate the string to be inserted into the + //: 'comm' field, and have the inner loop dictate the string to be + //: appended to the assembled string. + //: + //: 6 Parse that string with 'ProcStat::parseProcStatString' and + //: observe that the results are reasonable. + // -------------------------------------------------------------------- + +#if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_CYGWIN) + namespace TC = BetterParsingTest; + + if (verbose) cout << "TESTING COMM CONTAINING SPACES\n" + "==============================\n"; + + const bsl::size_t npos = bsl::string::npos; + + controlledCpuBurn(); + + const int pid = bdls::ProcessUtil::getProcessId(); + bsl::string procStat; + int rc = TC::ProcStats::readProcStatString(&procStat, pid); + ASSERT(0 == rc); + + if (veryVerbose) P(procStat); + + TC::testParseProcStatStr(procStat, pid); + + ASSERT(1 == bsl::count(procStat.begin(), procStat.end(), '(')); + const bsl::size_t openPos = procStat.find('('); + ASSERT(npos != openPos); + + const bsl::string FRONT = procStat.substr(0, openPos + 1); + + ASSERT(1 == bsl::count(procStat.begin(), procStat.end(), ')')); + const bsl::size_t closePos = procStat.find(')'); + ASSERT(npos != closePos); + ASSERT(openPos < closePos); + + const bsl::string BACK = procStat.substr(closePos); + + if (veryVerbose) { P(FRONT); P(BACK); } + + const char *strings[] = { "", "woof", "woof meow", "bow() wow()", + "))))", "((((", "!@#$%^&*()", + "()()()()()()()()", "0123456789abcdef", + "((((((((((((((((", "))))))))))))))))", + "very very very very very very very very" + " very very very very long string" }; + enum { k_NUM_STRINGS = sizeof strings / sizeof *strings }; + enum { k_LONG_STR_IDX = k_NUM_STRINGS - 1 }; + + for (int ti = 0; ti < k_NUM_STRINGS; ++ti) { + const bsl::string COMM = strings[ti]; + const bsl::string EXP_COMM = '(' + COMM + ')'; + + for (int tj = 0; tj < k_NUM_STRINGS; ++tj) { + const bsl::string TAIL = bsl::string(*strings[tj] ? " " : "") + + strings[tj]; + const bsl::string str = FRONT + COMM + BACK + TAIL; + + if (veryVeryVerbose) P(str); + + TC::testParseProcStatStr(str, pid, EXP_COMM.c_str(), + k_LONG_STR_IDX != ti); + } + } +#else + if (verbose) bsl::cout << + "There is no 'comm' field parsed on any platform other than\n" + "Linux, so the bug cannot occur on other platforms.\n" + "Test skipped.\n"; +#endif } break; case 10: { // -------------------------------------------------------------------- diff --git a/groups/bal/balb/balb_pipecontrolchannel.cpp b/groups/bal/balb/balb_pipecontrolchannel.cpp index 8dc4c85151..2233cd9391 100644 --- a/groups/bal/balb/balb_pipecontrolchannel.cpp +++ b/groups/bal/balb/balb_pipecontrolchannel.cpp @@ -100,12 +100,12 @@ int PipeControlChannel::sendEmptyMessage() HANDLE pipe; if (!WaitNamedPipeW(wPipeName.c_str(), NMPWAIT_USE_DEFAULT_WAIT)) { - return -1; + return -1; // RETURN } pipe = CreateFileW(wPipeName.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (INVALID_HANDLE_VALUE == pipe) { - return 2; + return 2; // RETURN } DWORD mode = PIPE_READMODE_MESSAGE; @@ -133,11 +133,11 @@ int PipeControlChannel::readNamedPipe() if (lastError != ERROR_PIPE_CONNECTED && lastError != ERROR_NO_DATA) { BSLS_LOG_TRACE("Failed to connect to named pipe '%s'", d_pipeName.c_str()); - return -1; + return -1; // RETURN } } - while(1) { + while (true) { char buffer[MAX_PIPE_BUFFER_LEN]; DWORD bytesRead = 0; @@ -147,19 +147,20 @@ int PipeControlChannel::readNamedPipe() &bytesRead, NULL)) { - if (bytesRead > 0) { - if (buffer[bytesRead - 1] == '\n') { - bytesRead--; - } - bslstl::StringRef stringRef(buffer, bytesRead); - if (!stringRef.isEmpty()) { - d_callback(stringRef); - } - } - else { - // reached EOF on a named pipe. - break; - } + if (bytesRead > 0) { + if (buffer[bytesRead - 1] == '\n') { + bytesRead--; + } + bslstl::StringRef stringRef(buffer, bytesRead); + if (!stringRef.isEmpty()) { + d_callback(stringRef); + } + } + else { + // reached EOF on a named pipe. + + break; + } } else { BSLS_LOG_TRACE("Failed read from named pipe '%s': %s", @@ -175,14 +176,14 @@ int PipeControlChannel::readNamedPipe() } int -PipeControlChannel::createNamedPipe(const bsl::string& pipeName) +PipeControlChannel::createNamedPipe(const char *pipeName) { - BSLS_LOG_TRACE("Creating named pipe '%s'", pipeName.c_str()); + BSLS_LOG_TRACE("Creating named pipe '%s'", pipeName); if (bdls::PipeUtil::isOpenForReading(pipeName)) { - BSLS_LOG_ERROR("Named pipe '%s' already exists", - pipeName.c_str()); - return -2; + BSLS_LOG_ERROR("Named pipe '%s' already exists", pipeName); + + return -2; // RETURN } bsl::wstring wPipeName; @@ -206,7 +207,7 @@ PipeControlChannel::createNamedPipe(const bsl::string& pipeName) BSLS_LOG_TRACE("Failed to create named pipe '%s': %s", d_pipeName.c_str(), describeWin32Error(GetLastError()).c_str()); - return -1; + return -1; // RETURN } return 0; @@ -357,7 +358,7 @@ PipeControlChannel::sendEmptyMessage() } int -PipeControlChannel::createNamedPipe(const bsl::string& pipeName) +PipeControlChannel::createNamedPipe(const char *pipeName) { // BSLS_ASSERT(0 == d_impl.d_unix.d_readFd); @@ -371,11 +372,11 @@ PipeControlChannel::createNamedPipe(const bsl::string& pipeName) // which case fail; otherwise unlink the pipe and continue. if (bdls::PipeUtil::isOpenForReading(pipeName)) { BSLS_LOG_ERROR("Named pipe '%s' is already in use by another " - "process", pipeName.c_str()); + "process", pipeName); return -2; // RETURN } - bdls::FilesystemUtil::remove(pipeName.c_str()); + bdls::FilesystemUtil::remove(pipeName); } bsl::string dirname; @@ -387,42 +388,39 @@ PipeControlChannel::createNamedPipe(const bsl::string& pipeName) } } - const char *rawPipeName = pipeName.c_str(); - - int rc = mkfifo(rawPipeName, 0666); - + int rc = mkfifo(pipeName, 0666); if (0 != rc) { int savedErrno = errno; BSLS_LOG_ERROR("Unable to create pipe '%s'. errno = %d (%s)", - rawPipeName, savedErrno, bsl::strerror(savedErrno)); + pipeName, savedErrno, bsl::strerror(savedErrno)); return -4; // RETURN } #if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_SOLARIS) - d_impl.d_unix.d_readFd = open(rawPipeName, + d_impl.d_unix.d_readFd = open(pipeName, O_RDONLY | O_NONBLOCK | O_CLOEXEC); #else - d_impl.d_unix.d_readFd = open(rawPipeName, O_RDONLY | O_NONBLOCK); + d_impl.d_unix.d_readFd = open(pipeName, O_RDONLY | O_NONBLOCK); #endif if (-1 == d_impl.d_unix.d_readFd) { int savedErrno = errno; BSLS_LOG_ERROR("Unable to open pipe '%s' for reading. errno = %d (%s)", - rawPipeName, savedErrno, bsl::strerror(savedErrno)); + pipeName, savedErrno, bsl::strerror(savedErrno)); return -5; // RETURN } #if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_SOLARIS) - d_impl.d_unix.d_writeFd = open(rawPipeName, O_WRONLY | O_CLOEXEC); + d_impl.d_unix.d_writeFd = open(pipeName, O_WRONLY | O_CLOEXEC); #else - d_impl.d_unix.d_writeFd = open(rawPipeName, O_WRONLY); + d_impl.d_unix.d_writeFd = open(pipeName, O_WRONLY); #endif if (-1 == d_impl.d_unix.d_writeFd) { int savedErrno = errno; BSLS_LOG_ERROR("Unable to open pipe '%s' for writing. errno = %d (%s)", - rawPipeName, savedErrno, bsl::strerror(savedErrno)); + pipeName, savedErrno, bsl::strerror(savedErrno)); return -6; // RETURN } @@ -431,7 +429,7 @@ PipeControlChannel::createNamedPipe(const bsl::string& pipeName) int savedErrno = errno; BSLS_LOG_ERROR( "Unable to set 'FD_CLOEXEC' on '%s' for reading. errno = %d (%s)", - rawPipeName, + pipeName, savedErrno, bsl::strerror(savedErrno)); return -8; // RETURN @@ -441,17 +439,18 @@ PipeControlChannel::createNamedPipe(const bsl::string& pipeName) int savedErrno = errno; BSLS_LOG_ERROR( "Unable to set 'FD_CLOEXEC' on '%s' for writing. errno = %d (%s)", - rawPipeName, + pipeName, savedErrno, bsl::strerror(savedErrno)); return -9; // RETURN } #endif - BSLS_LOG_TRACE("Created named pipe '%s'", rawPipeName); + BSLS_LOG_TRACE("Created named pipe '%s'", pipeName); return 0; } + } // close package namespace #endif // END PLATFORM-SPECIFIC FUNCTION IMPLEMENTATIONS @@ -500,16 +499,14 @@ void PipeControlChannel::backgroundProcessor() BSLS_LOG_TRACE("The background thread has stopped"); } -int PipeControlChannel::start(const bsl::string& pipeName) +int PipeControlChannel::start(const char *pipeName) { - bslmt::ThreadAttributes attributes; - return start(pipeName, attributes); + return start(pipeName, bslmt::ThreadAttributes()); } -int PipeControlChannel::start(const bsl::string& pipeName, - const bslmt::ThreadAttributes& attributes) +int PipeControlChannel::start(const char *pipeName, + const bslmt::ThreadAttributes& attributes) { - // BSLS_ASSERT(!d_isRunningFlag); // TBD: DOES createNamedPipe FAIL??? if (d_backgroundState == e_RUNNING) { return 2; // RETURN @@ -518,14 +515,15 @@ int PipeControlChannel::start(const bsl::string& pipeName, // See test driver case 9, on windows, createNamedPipe does not // fail if the named pipe is already open!!!!! // TBD + if (d_isPipeOpen) { - return 3; + return 3; // RETURN } #endif if (0 != createNamedPipe(pipeName)) { - BSLS_LOG_ERROR("Unable to create named pipe '%s'", pipeName.c_str()); - return 1; // RETURN + BSLS_LOG_ERROR("Unable to create named pipe '%s'", pipeName); + return 1; // RETURN } d_pipeName = pipeName; @@ -556,6 +554,7 @@ void PipeControlChannel::shutdown() if (bslmt::ThreadUtil::self() == d_thread) { // When 'shutdown' is called from the same thread as the background // thread perform a synchronous shutdown. + return; // RETURN } diff --git a/groups/bal/balb/balb_pipecontrolchannel.h b/groups/bal/balb/balb_pipecontrolchannel.h index 6234d9e2bf..bd7ef3d536 100644 --- a/groups/bal/balb/balb_pipecontrolchannel.h +++ b/groups/bal/balb/balb_pipecontrolchannel.h @@ -18,7 +18,7 @@ BSLS_IDENT("$Id: $") //@CLASSES: // balb::PipeControlChannel: Mechanism for reading messages from a named pipe // -//@SEE_ALSO: bdlsu::PipeUtil +//@SEE_ALSO: bdls_pipeutil // //@DESCRIPTION: This component provides a platform-independent mechanism, // 'balb::PipeControlChannel', for establishing, monitoring, and shutting down @@ -183,6 +183,8 @@ BSLS_IDENT("$Id: $") #include #include +#include + #include #include @@ -250,7 +252,7 @@ class PipeControlChannel { // Loop while d_isRunningFlag is true, reading and dispatching messages // from the named pipe. - int createNamedPipe(const bsl::string& pipeName); + int createNamedPipe(const char *pipeName); // Open a named pipe having the specified 'pipeName'. Return 0 on // success, and a non-zero value otherwise. @@ -292,9 +294,14 @@ class PipeControlChannel { // clean up any associated system resources. // MANIPULATORS - int start(const bsl::string& pipeName); - int start(const bsl::string& pipeName, - const bslmt::ThreadAttributes& attributes); + int start(const char *pipeName); + template + int start(const STRING_TYPE& pipeName); + int start(const char *pipeName, + const bslmt::ThreadAttributes& attributes); + template + int start(const STRING_TYPE& pipeName, + const bslmt::ThreadAttributes& attributes); // Open a named pipe having the specified 'pipeName', and start a // thread to read messages and dispatch them to the callback specified // at construction. Optionally specify 'attributes' of the background @@ -302,7 +309,9 @@ class PipeControlChannel { // constructed 'ThreadAttributes' object will be used. Return 0 on // success, and a non-zero value otherwise. In particular, return a // non-zero value if the pipe cannot be opened or if it is detected - // that another process is reading from the pipe. + // that another process is reading from the pipe. 'pipeName' must be + // of the types 'const char *', 'char *', 'bsl::string', 'std::string', + // 'std::pmr::string' (if supported), or 'bslstl::StringRef'. void shutdown(); // Stop reading from the pipe and dispatching messages. Note that this @@ -317,6 +326,65 @@ class PipeControlChannel { // Return the fully qualified system name of the pipe. }; + // ==================================== + // class PipeControlChannel_CStringUtil + // ==================================== + +struct PipeControlChannel_CStringUtil { + // This component-private utility 'struct' provides a namespace for the + // 'flatten' overload set intended to be used in concert with an overload + // set consisting of a function template with a deduced argument and an + // non-template overload accepting a 'const char *'. The actual + // implementation of the functionality would be in the 'const char *' + // overload whereas the purpose of the function template is to invoke the + // 'const char *' overload with a null-terminated string. + // + // The function template achieves null-termination by recursively calling + // the function and supplying it with the result of 'flatten' invoked on + // the deduced argument. This 'flatten' invocation will call 'c_str()' on + // various supported 'string' types, will produce a temporary 'bsl::string' + // for possibly non-null-terminated 'bslstl::StringRef', and will result in + // a 'BSLMF_ASSERT' for any unsupported type. Calling the function with + // the temporary 'bsl::string' produced from 'bslstl::StringRef' will + // result in a second invocation of 'flatten', this time producing + // 'const char *', and finally calling the function with a null-terminated + // string. + // + // Note that the 'bslstl::StringRef' overload for 'flatten' is provided for + // backwards compatibility. Without it, the 'bsl::string' and + // 'std::string' overloads would be ambiguous. In new code, it is + // preferable to not provide 'bslstl::StringRef' overload in a similar + // facility and require the clients to explicitly state the string type in + // their code, making a potential allocation obvious. The + // 'bsl::string_view' overload is not provided for the same reason. + // + // Also note that since the constructor for 'string' types from + // 'bsl::string_view' is explicit, it is not necessary to support + // 'bsl::string_view' for backwards compatibility, and it is not supported. + + // CLASS METHODS + + static const char *flatten(char *cString); + static const char *flatten(const char *cString); + // Return the specified 'cString'. + + static const char *flatten(const bsl::string& string); + static const char *flatten(const std::string& string); +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR + static const char *flatten(const std::pmr::string& string); +#endif + // Return the result of invoking 'c_str()' on the specified 'string'. + + static bsl::string flatten(const bslstl::StringRef& stringRef); + // Return a temporary 'bsl::string' constructed from the specified + // 'stringRef'. + + template + static const char *flatten(const TYPE&); + // Produce a compile-time error informing the caller that the + // parameterized 'TYPE' is not supported as the parameter for the call. +}; + // ============================================================================ // INLINE DEFINITIONS // ============================================================================ @@ -327,6 +395,74 @@ const bsl::string& PipeControlChannel::pipeName() const return d_pipeName; } +template +int PipeControlChannel::start(const STRING_TYPE& pipeName) +{ + return start(PipeControlChannel_CStringUtil::flatten(pipeName), + bslmt::ThreadAttributes()); +} + +template +int PipeControlChannel::start(const STRING_TYPE& pipeName, + const bslmt::ThreadAttributes& threadAttributes) +{ + return start(PipeControlChannel_CStringUtil::flatten(pipeName), + threadAttributes); +} + + // ------------------------------------ + // class PipeControlChannel_CStringUtil + // ------------------------------------ + +// CLASS METHODS +inline +const char *PipeControlChannel_CStringUtil::flatten(char *cString) +{ + return cString; +} + +inline +const char *PipeControlChannel_CStringUtil::flatten(const char *cString) +{ + return cString; +} + +inline +const char *PipeControlChannel_CStringUtil::flatten(const bsl::string& string) +{ + return string.c_str(); +} + +inline +const char *PipeControlChannel_CStringUtil::flatten(const std::string& string) +{ + return string.c_str(); +} + +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR +inline +const char *PipeControlChannel_CStringUtil::flatten( + const std::pmr::string& string) +{ + return string.c_str(); +} +#endif + +inline +bsl::string PipeControlChannel_CStringUtil::flatten( + const bslstl::StringRef& stringRef) +{ + return stringRef; +} + +template +inline +const char *PipeControlChannel_CStringUtil::flatten(const TYPE&) +{ + BSLMF_ASSERT(("Unsupported parameter type." && !sizeof(TYPE))); + return 0; +} + } // close package namespace } // close enterprise namespace #endif diff --git a/groups/bal/balb/balb_pipecontrolchannel.t.cpp b/groups/bal/balb/balb_pipecontrolchannel.t.cpp index 265109ccda..8bc884407d 100644 --- a/groups/bal/balb/balb_pipecontrolchannel.t.cpp +++ b/groups/bal/balb/balb_pipecontrolchannel.t.cpp @@ -20,12 +20,10 @@ #include #include +#include +#include #include - -#include -#include -#include -#include +#include #include @@ -35,9 +33,15 @@ #include #include +#include +#include +#include +#include +#include +#include + #ifdef BSLS_PLATFORM_OS_UNIX #include -#include #include #include @@ -131,10 +135,44 @@ void aSsErT(bool condition, const char *message, int line) // ============================================================================ // GLOBAL TYPES, CONSTANTS, AND VARIABLES FOR TESTING // ---------------------------------------------------------------------------- -static int verbose = 0; -static int veryVerbose = 0; -static int veryVeryVerbose = 0; -static int veryVeryVeryVerbose = 0; + +typedef bdls::FilesystemUtil FUtil; + +int verbose = 0; +int veryVerbose = 0; +int veryVeryVerbose = 0; +int veryVeryVeryVerbose = 0; + +enum PipeNameForm { e_BEGIN + , e_CONST_CHAR_STAR = e_BEGIN + , e_BSL_STRING + , e_STD_STRING +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR + , e_PMR_STRING +#endif + , e_STRING_REF + , e_END }; + +bsl::ostream& operator<<(bsl::ostream& stream, PipeNameForm pnf) +{ + const char *str = e_CONST_CHAR_STAR == pnf + ? "'const char *'" + : e_BSL_STRING == pnf + ? "bsl::string" + : e_STD_STRING == pnf + ? "std::string" +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR + : e_PMR_STRING == pnf + ? "pmr::string" +#endif + : e_STRING_REF == pnf + ? "bslstl::StringRef" + : e_END == pnf + ? "end" + : "unknown"; + + return stream << str; +} extern "C" { @@ -166,7 +204,8 @@ class ControlServer { } } - // UNIMPLEMENTED + private: + // NOT IMPLEMENTED ControlServer(const ControlServer&); // = deleted ControlServer& operator=(const ControlServer&); // = deleted @@ -181,24 +220,29 @@ class ControlServer { {} // MANIPULATORS - int start(const bslstl::StringRef &pipeName) { + int start(const bslstl::StringRef &pipeName) + { return d_channel.start(pipeName); } - void shutdown() { + void shutdown() + { d_channel.shutdown(); } - void stop() { + void stop() + { d_channel.stop(); } // ACCESSORS - bsl::size_t numMessages() const { + bsl::size_t numMessages() const + { return d_messages.size(); } - const bsl::string& message(int index) const { + const bsl::string& message(int index) const + { return d_messages[index]; } }; @@ -264,7 +308,8 @@ void loadData(bsl::string *result, int length) } // close unnamed namespace extern "C" -void onSigPipe(int) { +void onSigPipe(int) +{ BSLS_LOG_WARN("SIGPIPE received"); } @@ -341,8 +386,21 @@ int main(int argc, char *argv[]) devnull.open("/dev/null"); } #endif + + // Pipe names are global to the CPU, among all processes. A matrix build + // will often be running the same test driver multiple times at once, so + // the pipe name has to contain the pid. Just containing the test case # + // won't be enough. 'pipeName' is used in many test cases. + + bsl::string pipeName; if (test >= 0) { cout << "TEST " << __FILE__ << " CASE " << test << endl; + + char buffer[200]; + bsl::sprintf(buffer, "ctrl.pcctest.%d.%d", test, + bdls::ProcessUtil::getProcessId()); + ASSERT(0 == bdls::PipeUtil::makeCanonicalName(&pipeName, buffer)); + if (veryVerbose) bsl::cerr << "pipeName: " << pipeName << bsl::endl; } #ifndef BSLS_PLATFORM_OS_WINDOWS @@ -352,7 +410,7 @@ int main(int argc, char *argv[]) switch (test) { case 0: case 12: { // -------------------------------------------------------------------- - // TESTING THREAD ATTRIBUTES PASSING + // TESTING START, THREAD ATTRIBUTES PASSING // // Concerns: //: 1 The 'start' method correctly passes required thread attributes to @@ -364,70 +422,169 @@ int main(int argc, char *argv[]) //: the desired thread configuration. Pass some messages using that //: mechanism and verify, that created tread has the expected //: attributes. + //: + //: 2 Loop, testing all overloads of 'start', with and without the + //: thread attributes passed, and with all 5 types of pipe name. // // Testing // int start(const bsl::string&, const bslmt::ThreadAttributes&); //--------------------------------------------------------------------- if (verbose) { - cout << "TESTING THREAD ATTRIBUTES PASSING" << endl - << "=================================" << endl; + cout << "TESTING START, THREAD ATTRIBUTES PASSING\n" + "========================================\n"; + } + + +#if defined(BSLS_PLATFORM_OS_LINUX) + // If no threadname is specified, the thread name is up to the first 15 + // bytes of the basename of 'argv[0]'. + + bsl::string dfltThreadName = argv[0]; + { + bsl::string s; + ASSERT(0 == bdls::PathUtil::getBasename(&s, dfltThreadName)); + dfltThreadName = s; } + dfltThreadName.resize(bsl::min( + 15, dfltThreadName.length())); + const bsl::string& DFLT_THREAD_NAME(dfltThreadName); +#else + // If no threadname is specified, the thread name is "". + const bsl::string DFLT_THREAD_NAME; +#endif - bsl::string pipeName; + const bsl::string RAW_EXP_THREAD_NAME("ctrl.attrtest12" ); + const bsl::string MSG ("this is the test message"); - ASSERT(0 == bdls::PipeUtil::makeCanonicalName( - &pipeName, - "ctrl.attributestest12")); + bslmt::ThreadAttributes rawAttributes; + ASSERT(rawAttributes.threadName().empty()); + rawAttributes.setThreadName(RAW_EXP_THREAD_NAME); + const bslmt::ThreadAttributes SET_ATTR(rawAttributes); - const bsl::string EXPECTED_THREAD_NAME("ctrl.attrtest12" ); - const bsl::string MSG ("this is the test message"); - bsl::string threadName; - bsl::string message; - bslmt::Barrier barrier(2); + const bsl::string PIPE_NAME_BSL(pipeName); - balb::PipeControlChannel pipeChannel( - bdlf::BindUtil::bind(&attributesTestCallback, - &threadName, - &message, - bdlf::PlaceHolders::_1, - &barrier)); + for (int attrI = 0; attrI < 2; ++attrI) { + const bool PASS_ATTRIBUTES = attrI; + const bsl::string& EXP_THREAD_NAME = PASS_ATTRIBUTES + ? RAW_EXP_THREAD_NAME + : DFLT_THREAD_NAME; - bslmt::ThreadAttributes attributes; - attributes.setThreadName(EXPECTED_THREAD_NAME); + for (int pnfi = e_BEGIN; pnfi < e_END; ++pnfi) { + const PipeNameForm PIPE_NAME_FORM = static_cast( + pnfi); - int rc = pipeChannel.start(pipeName, attributes); + bsl::string threadName; + bsl::string message; + bslmt::Barrier barrier(2); - if (0 == rc) { - rc = bdls::PipeUtil::send(pipeName, MSG + "\n"); - ASSERTV(rc, 0 == rc); - } + FUtil::remove(pipeName); - // Wait for background processing thread. - barrier.wait(); + balb::PipeControlChannel pipeChannel( + bdlf::BindUtil::bind(&attributesTestCallback, + &threadName, + &message, + bdlf::PlaceHolders::_1, + &barrier)); + + int rc = -1; + if (PASS_ATTRIBUTES) { + switch (PIPE_NAME_FORM) { + case e_CONST_CHAR_STAR: { + rc = pipeChannel.start(PIPE_NAME_BSL.c_str(), + SET_ATTR); + } break; + case e_BSL_STRING: { + rc = pipeChannel.start(PIPE_NAME_BSL, SET_ATTR); + } break; + case e_STD_STRING: { + const std::string PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME, SET_ATTR); + } break; +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR + case e_PMR_STRING: { + const std::pmr::string PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME, SET_ATTR); + } break; +#endif + case e_STRING_REF: { + const bslstl::StringRef PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME, SET_ATTR); + } break; + default: { + ASSERTV(pnfi, 0); + } + } + } + else { + switch (PIPE_NAME_FORM) { + case e_CONST_CHAR_STAR: { + rc = pipeChannel.start(PIPE_NAME_BSL.c_str()); + } break; + case e_BSL_STRING: { + rc = pipeChannel.start(PIPE_NAME_BSL); + } break; + case e_STD_STRING: { + const std::string PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME); + } break; +#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR + case e_PMR_STRING: { + const std::pmr::string PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME); + } break; +#endif + case e_STRING_REF: { + const bslstl::StringRef PIPE_NAME(PIPE_NAME_BSL); + rc = pipeChannel.start(PIPE_NAME); + } break; + default: { + ASSERTV(pnfi, 0); + } + } + } + + ASSERT(0 == rc); + if (0 == rc) { + rc = bdls::PipeUtil::send(PIPE_NAME_BSL, MSG + "\n"); + ASSERTV(rc, 0 == rc); + } + + // Wait for background processing thread. + barrier.wait(); - pipeChannel.shutdown(); - pipeChannel.stop(); + pipeChannel.shutdown(); + pipeChannel.stop(); - ASSERTV(message, MSG == message); + ASSERTV(message, MSG == message); #if defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_DARWIN) || \ defined(BSLS_PLATFORM_OS_SOLARIS) - ASSERTV(threadName, EXPECTED_THREAD_NAME == threadName); + ASSERTV(PIPE_NAME_FORM, threadName, + EXP_THREAD_NAME == threadName); #elif defined(BSLS_PLATFORM_OS_WINDOWS) - // The threadname will only be visible if we're running on Windows 10, - // version 1607 or later, otherwise it will be empty. It breaks the - // test strategy, since we can not to rely on the thread name value. - // But since the behavior being tested is common for all platforms, we - // dare to say that it is correct on Windows if the tests pass - // successfully for other platforms. - - ASSERTV(threadName, EXPECTED_THREAD_NAME == threadName - || true == threadName.empty()); + // The threadname will only be visible if we're running on + // Windows 10, version 1607 or later, otherwise it will be + // empty. It breaks the test strategy, since we can not to + // rely on the thread name value. But since the behavior being + // tested is common for all platforms, we dare to say that it + // is correct on Windows if the tests pass successfully for + // other platforms. + + if (PASS_ATTRIBUTES) { + ASSERTV(PIPE_NAME_FORM, threadName, + threadName.empty() || EXP_THREAD_NAME == threadName); + } + else { + ASSERTV(PIPE_NAME_FORM, threadName, + threadName.empty() || DFLT_THREAD_NAME == threadName); + } #else // Thread names are not supported for other platforms. - ASSERTV(threadName, true == threadName.empty()); + ASSERTV(threadName, threadName.empty()); #endif + } + } } break; case 11: { // -------------------------------------------------------------------- @@ -478,7 +635,6 @@ int main(int argc, char *argv[]) cout << "Skipping test case 11 on non-Windows OS..." << endl; } #else - const char PIPE_NAME[] = "\\\\.\\pipe\\ctrl.balb.pcctest11"; bslmt::Mutex mutex; bslmt::Condition condition; bsls::AtomicBool conditionFlag(false); @@ -488,10 +644,10 @@ int main(int argc, char *argv[]) &conditionFlag, bdlf::PlaceHolders::_1)); - pipeChannel.start(PIPE_NAME); + pipeChannel.start(pipeName); bslmt::ThreadUtil::Handle thread; int rc = bslmt::ThreadUtil::create(&thread, bdlf::BindUtil::bind( - &sendHello, PIPE_NAME)); + &sendHello, pipeName.c_str())); ASSERT(rc == 0); if (rc != 0) { break; // BREAK @@ -527,7 +683,7 @@ int main(int argc, char *argv[]) if (verbose) { cerr << "Case 10 client process starting" << endl; } - unlink(argv[3]); + FUtil::remove(argv[3]); bslmt::Barrier barrier(2); balb::PipeControlChannel channel(bdlf::BindUtil::bind( @@ -565,11 +721,6 @@ int main(int argc, char *argv[]) << "==========" << endl; } - bsl::string pipeName; - - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.pcctest10")); - char buffer[512]; char verb = veryVeryVerbose ? 'A' : verbose ? 'Z' : 'Y'; @@ -629,9 +780,7 @@ int main(int argc, char *argv[]) cerr << "Case 9 client process starting" << endl; } -#ifdef BSLS_PLATFORM_OS_UNIX - unlink(argv[3]); -#endif + FUtil::remove(argv[3]); const char MESSAGE[] = "Hello, world! The sick cat couldn't jump over even a lazy dog"; @@ -681,9 +830,7 @@ int main(int argc, char *argv[]) channel.stop(); bslmt::ThreadUtil::sleep(bsls::TimeInterval(1.0)); - - break; - } + } break; case 9: { // -------------------------------------------------------------------- // TESTING PIPE-IN-USE SAFETY @@ -703,11 +850,6 @@ int main(int argc, char *argv[]) << "=======================" << endl; } - bsl::string pipeName; - - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.safttest9")); - char buffer[512]; snprintf(buffer, 512, "%s -9 %c %s", argv[0], verbose? 'Z' : 'Y', pipeName.c_str()); @@ -727,7 +869,7 @@ int main(int argc, char *argv[]) int rc; for (int i = 0; i < 5; ++i) { rc = server.start(pipeName); - LOOP_ASSERT(i, 0 != rc); + ASSERTV(i, pipeName, 0 != rc); bslmt::ThreadUtil::microSleep(100); } @@ -757,9 +899,7 @@ int main(int argc, char *argv[]) cerr << "Case 8 client process starting" << endl; } -#ifdef BSLS_PLATFORM_OS_UNIX - unlink(argv[3]); -#endif + FUtil::remove(argv[3]); balb::PipeControlChannel *channel = new balb::PipeControlChannel(&noop); @@ -777,8 +917,7 @@ int main(int argc, char *argv[]) #endif ASSERT(!"unreachable"); - break; - } + } break; case 8: { // -------------------------------------------------------------------- // TESTING CRASH RECOVERY @@ -798,11 +937,6 @@ int main(int argc, char *argv[]) << "======================================" << endl; } - bsl::string pipeName; - - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.restarttest8")); - char buffer[512]; snprintf(buffer, 512, "%s -8 %c %s", argv[0], verbose? 'Z' : 'Y', pipeName.c_str()); @@ -873,8 +1007,10 @@ int main(int argc, char *argv[]) // Now, construct and run the server using a canonical name for the pipe: //.. +#if 0 // 'pipeName' in test driver (but not most situations) must contain pid. bsl::string pipeName; bdls::PipeUtil::makeCanonicalName(&pipeName, "ctrl.pcctest"); +#endif ControlServer server; @@ -940,13 +1076,8 @@ int main(int argc, char *argv[]) bslma::TestAllocator ta(veryVeryVeryVerbose); { - bsl::string pipeName; + FUtil::remove(pipeName.c_str()); - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.balb.pcctest6")); -#ifdef BSLS_PLATFORM_OS_UNIX - unlink(pipeName.c_str()); -#endif enum { NUM_CLIENTS = 10, NUM_ITERATIONS = 100 }; int DATA[] = { @@ -1025,13 +1156,7 @@ int main(int argc, char *argv[]) bslma::TestAllocator ta(veryVeryVeryVerbose); { - bsl::string pipeName; - - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.balb.pcctest5")); -#ifdef BSLS_PLATFORM_OS_UNIX - unlink(pipeName.c_str()); -#endif + FUtil::remove(pipeName.c_str()); const char MESSAGE[] = "Hello, world!"; @@ -1081,19 +1206,13 @@ int main(int argc, char *argv[]) bslma::TestAllocator ta(veryVeryVeryVerbose); { - bsl::string pipeName; - - ASSERT(0 == bdls::PipeUtil::makeCanonicalName - (&pipeName, "ctrl.balb.pcctest4")); -#ifdef BSLS_PLATFORM_OS_UNIX - unlink(pipeName.c_str()); -#endif + FUtil::remove(pipeName); { - balb::PipeControlChannel channel(&noop, &ta); + balb::PipeControlChannel channel(&noop, &ta); - int rc = channel.start(pipeName); - ASSERT(0 == rc); + int rc = channel.start(pipeName); + ASSERT(0 == rc); } ASSERT("The calling thread is unblocked."); @@ -1122,16 +1241,11 @@ int main(int argc, char *argv[]) bslma::TestAllocator ta(veryVeryVeryVerbose); { -#ifdef BSLS_PLATFORM_OS_WINDOWS - const char PIPE_NAME[] = "\\\\.\\pipe\\ctrl.balb.pcctest3"; -#else - const char PIPE_NAME[] = "/tmp/ctrl.balb.pcctest3"; - unlink(PIPE_NAME); -#endif + FUtil::remove(pipeName); balb::PipeControlChannel channel(&noop, &ta); - int rc = channel.start(PIPE_NAME); + int rc = channel.start(pipeName); ASSERT(0 == rc); channel.shutdown(); @@ -1171,38 +1285,34 @@ int main(int argc, char *argv[]) { int rc; -#ifdef BSLS_PLATFORM_OS_WINDOWS - const char PIPE_NAME1[] = "\\\\.\\pipe\\ctrl.pcctest2-1"; - const char PIPE_NAME2[] = "\\\\.\\pipe\\ctrl.pcctest2-2"; -#else - const char PIPE_NAME1[] = "/tmp/ctrl.balb.pcctest2-1"; - const char PIPE_NAME2[] = "/tmp/ctrl.balb.pcctest2-2"; - unlink(PIPE_NAME1); - unlink(PIPE_NAME2); -#endif + const bsl::string pipeName1 = pipeName + "-1"; + const bsl::string pipeName2 = pipeName + "-2"; + + FUtil::remove(pipeName1); + FUtil::remove(pipeName2); balb::PipeControlChannel channel(&noop, &ta); - rc = channel.start(PIPE_NAME1); + rc = channel.start(pipeName1); ASSERT(0 == rc); - ASSERT(PIPE_NAME1 == channel.pipeName()); + ASSERT(pipeName1 == channel.pipeName()); channel.shutdown(); channel.stop(); - rc = channel.start(PIPE_NAME2); + rc = channel.start(pipeName2); ASSERT(0 == rc); - ASSERT(PIPE_NAME2 == channel.pipeName()); + ASSERT(pipeName2 == channel.pipeName()); channel.shutdown(); channel.stop(); - rc = channel.start(PIPE_NAME2); + rc = channel.start(pipeName2); ASSERT(0 == rc); - ASSERT(PIPE_NAME2 == channel.pipeName()); + ASSERT(pipeName2 == channel.pipeName()); channel.shutdown(); channel.stop(); @@ -1233,19 +1343,13 @@ int main(int argc, char *argv[]) bslma::TestAllocator ta(veryVeryVeryVerbose); { -#ifdef BSLS_PLATFORM_OS_WINDOWS - const char PIPE_NAME[] = "\\\\.\\pipe\\ctrl.balb.pcctest1"; -#else - const char PIPE_NAME[] = "/tmp/ctrl.balb.pcctest1"; - unlink(PIPE_NAME); -#endif + FUtil::remove(pipeName); balb::PipeControlChannel channel(&noop, &ta); - - int rc = channel.start(PIPE_NAME); + int rc = channel.start(pipeName); ASSERT(0 == rc); - ASSERT(PIPE_NAME == channel.pipeName()); + ASSERT(pipeName == channel.pipeName()); channel.shutdown(); channel.stop(); diff --git a/groups/bal/balb/balb_ratelimiter.cpp b/groups/bal/balb/balb_ratelimiter.cpp new file mode 100644 index 0000000000..2a11e1b3c7 --- /dev/null +++ b/groups/bal/balb/balb_ratelimiter.cpp @@ -0,0 +1,141 @@ +// balb_ratelimiter.cpp -*-C++-*- +#include + +#include +BSLS_IDENT_RCSID(balb_ratelimiter.cpp, "$Id$ $CSID$") + +namespace BloombergLP { +namespace balb { + + //------------------ + // class RateLimiter + //------------------ + +// CREATORS +RateLimiter::RateLimiter(bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow, + const bsls::TimeInterval& currentTime) +: d_peakRateBucket(1, 1, currentTime) +, d_sustainedRateBucket(1, 1, currentTime) +{ + setRateLimits(sustainedRateLimit, + sustainedRateWindow, + peakRateLimit, + peakRateWindow); +} + +RateLimiter::~RateLimiter() +{ + BSLS_ASSERT_SAFE(sustainedRateLimit() > 0); + BSLS_ASSERT_SAFE(peakRateLimit() > 0); + + BSLS_ASSERT_SAFE(sustainedRateWindow() > bsls::TimeInterval(0)); + BSLS_ASSERT_SAFE(peakRateWindow() > bsls::TimeInterval(0)); + + BSLS_ASSERT_SAFE(peakRateLimit() == 1 || + peakRateWindow() <= + LeakyBucket::calculateDrainTime( + ULLONG_MAX, peakRateLimit(), true)); + + BSLS_ASSERT_SAFE(sustainedRateLimit() == 1 || + sustainedRateWindow() <= + LeakyBucket::calculateDrainTime( + ULLONG_MAX, sustainedRateLimit(), true)); +} + +// CLASS METHODS +namespace { +bool supportsExactly(bsls::Types::Uint64 limit, + const bsls::TimeInterval& window) + // Return 'true' if the specified 'limit' and 'window' are legal values + // with which to initialize a 'balb::LeakyBucket' object, and if so, + // whether a 'balb::LeakyBucket' object so initialized would preserve the + // value of 'window'. +{ + // Aside from checking that the capacity calculated from 'window' and + // 'limit' can back out the same 'window' value, we also include checks on + // the parameters that the functions called do as assertions, so that this + // function will return 'false' for those values, e.g., 'window' is large + // enough to cause integer overflow. + return limit > 0 && window > bsls::TimeInterval() && + (limit == 1 || window <= LeakyBucket::calculateDrainTime( + ULLONG_MAX, limit, true)) && + window == LeakyBucket::calculateTimeWindow( + limit, LeakyBucket::calculateCapacity(limit, window)); +} + +} // close unnamed namespace + +bool RateLimiter::supportsRateLimitsExactly( + bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow) +{ + return supportsExactly(sustainedRateLimit, sustainedRateWindow) && + supportsExactly(peakRateLimit, peakRateWindow); +} + +// MANIPULATORS +bsls::TimeInterval RateLimiter::calculateTimeToSubmit( + const bsls::TimeInterval& currentTime) +{ + bsls::TimeInterval timeToSubmitPeak = + d_peakRateBucket.calculateTimeToSubmit(currentTime); + + bsls::TimeInterval timeToSubmitSustained = + d_sustainedRateBucket.calculateTimeToSubmit(currentTime); + + return bsl::max(timeToSubmitPeak, timeToSubmitSustained); +} + +void RateLimiter::setRateLimits(bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow) +{ + BSLS_ASSERT(sustainedRateLimit > 0); + BSLS_ASSERT(peakRateLimit > 0); + + BSLS_ASSERT(sustainedRateWindow > bsls::TimeInterval(0)); + BSLS_ASSERT(peakRateWindow > bsls::TimeInterval(0)); + + BSLS_ASSERT(peakRateLimit == 1 || + peakRateWindow <= LeakyBucket::calculateDrainTime( + ULLONG_MAX, peakRateLimit, true)); + + BSLS_ASSERT(sustainedRateLimit == 1 || + sustainedRateWindow <= + LeakyBucket::calculateDrainTime( + ULLONG_MAX, sustainedRateLimit, true)); + + bsls::Types::Uint64 capacity = LeakyBucket::calculateCapacity( + sustainedRateLimit, sustainedRateWindow); + + d_sustainedRateBucket.setRateAndCapacity(sustainedRateLimit, capacity); + + capacity = LeakyBucket::calculateCapacity(peakRateLimit, peakRateWindow); + + d_peakRateBucket.setRateAndCapacity(peakRateLimit, capacity); +} + +} // close package namespace +} // close enterprise namespace + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_ratelimiter.h b/groups/bal/balb/balb_ratelimiter.h new file mode 100644 index 0000000000..5b976bbea0 --- /dev/null +++ b/groups/bal/balb/balb_ratelimiter.h @@ -0,0 +1,673 @@ +// balb_ratelimiter.h -*-C++-*- +#ifndef INCLUDED_BALB_RATELIMITER +#define INCLUDED_BALB_RATELIMITER + +#include +BSLS_IDENT("$Id: $") + +//@PURPOSE: Provide a mechanism to limit peak and sustained consumption rates. +// +//@CLASSES: +// balb::RateLimiter: mechanism to monitor resource consumption rates +// +//@SEE ALSO balb_leakybucket +// +//@DESCRIPTION: This component provides a mechanism, 'balb::RateLimiter', that +// enables clients to monitor and control the use of a resource such that the +// peak consumption rate and the sustained consumption rate do not exceed their +// respective configured limits. +// +// The limits on resource consumption rates of a 'balb::RateLimiter' object are +// configured using a specified peak rate (measured in 'units/s') along with +// its time-window, and a specified sustained rate (measured in 'units/s') +// along with its time-window. The peak-rate time-window indicates a sliding +// time period over which the average consumption rate shall not exceed the +// peak-rate; similarly, the sustained-rate time-window indicates a sliding +// time period over which the average consumption rate shall not exceed the +// sustained rate. 'unit' is a generic unit of measurement (e.g., bytes, +// megabytes, number of messages, packets, liters, clock cycles, etc.). +// +///Internal Model +///-------------- +// Internally, a rate limiter (currently) models resource usage using two +// corresponding 'balb::LeakyBucket' objects, one for limiting peak resource +// usage and one for limiting sustained resource usage. Each leaky bucket +// provides an approximation for a moving total, where the configured time +// window corresponds to the period of the moving total, and that time window +// multiplied by the corresponding rate indicates the sum that the moving total +// may not exceed (i.e., the capacity of the leaky bucket). As the units are +// submitted to a rate limiter, they are added to both the peak and sustained +// rate moving-totals, and then removed over time at the corresponding +// configured rate. +// +// Figure 1 illustrates the behavior of a rate limiter during a typical usage +// scenario using moving-totals: +//.. +// Fig. 1: +// +// Rp (peak rate) = 2 units/s +// Wp (peak-rate time-window) = 2 s +// Rs (sustained rate) = 1 units/s +// Ws (sustained-rate time-window) = 7 s +// +// Submit 5 Submit 7 +// +// | | | | | | | | | | 7|~~~| +// 12| | 6| | 12| | 6| | 12| | 6|~~~| +// 11| | 5|~~~| 11| | 5| | 11| | 5|~~~| +// 10| | Lp-4|~~~| 10| | Lp-4|---| 10| | Lp-4|~~~| +// 9| | 3|~~~| 9| | 3| | 9|~~~| 3|~~~| +// 8| | 2|~~~| 8| | 2| | 8|~~~| 2|~~~| +// Ls-7|---| 1|~~~| Ls-7|---| 1|~~~| Ls-7|~~~| 1|~~~| +// 6| | +- -+ 6| | +- -+ 6|~~~| +- -+ +// 5|~~~| 5| | 5|~~~| +// 4|~~~| 4| | 4|~~~| +// 3|~~~| 3|~~~| 3|~~~| +// 2|~~~| 2|~~~| 2|~~~| +// 1|~~~| 1|~~~| 1|~~~| +// +- -+ +- -+ +- -+ +// +// Time: t0 t0 + 2s t0 + 2s +// +// +// Submit 2 +// +// | | 7| | | | 7| | | | 7| | +// 12| | 6| | 12| | 6| | 12| | 6| | +// 11| | 5| | 11| | 5| | 11| | 5| | +// 10| | Lp-4|---| 10| | Lp-4|---| 10| | Lp-4|---| +// 9| | 3|~~~| 9| | 3| | 9| | 3|~~~| +// 8| | 2|~~~| 8| | 2| | 8| | 2|~~~| +// Ls-7|~~~| 1|~~~| Ls-7|---| 1|~~~| Ls-7|~~~| 1|~~~| +// 6|~~~| +- -+ 6|---| +- -+ 6|~~~| +- -+ +// 5|~~~| 5|~~~| 5|~~~| +// 4|~~~| 4|~~~| 4|~~~| +// 3|~~~| 3|~~~| 3|~~~| +// 2|~~~| 2|~~~| 2|~~~| +// 1|~~~| 1|~~~| 1|~~~| +// +- -+ +- -+ +- -+ +// +// Time: t0 + 4s t0 + 6s t0 + 6s +//.. +// Suppose we have a rate limiter with a peak rate of 'Rp = 2 units/s', a +// peak-rate time-window of 'Wp = 2 s', a sustained rate of 'Rs = 1 units/s', +// and a sustained-rate time-window of 'Ws = 7 s'. +// +// This rate limiter maintains a moving-total having a capacity +// 'Lp = Rp * Wp = 4 units' that controls the peak rate and another +// moving-total having a capacity 'Ls = Rs * Ws = 7 units' that controls the +// sustained rate. +// +// Figure 1 shows the following sequence of events: +//: o At time 't0s', we submit 5 units. The submitted units are added to the +//: both moving-totals, and as a result the 'Lp' is exceeded, which means +//: that the average consumption rate over the peak-rate time-window has +//: exceeded the peak rate. Note that we can not submit any more units at +//: this time even though 'Ls' is not exceeded (the average consumption rate +//: over the sustained-rate time-windows has not exceeded the sustained +//: rate). +//: +//: o At time 't0 + 2s' the number of units contained moving-totals are +//: recalculated. As a result, 4 units ('Rp * 2 s') are subtracted from the +//: peak rate moving-total, and 2 units ('Rs * 2 s') are subtracted from the +//: sustained rate moving-total. Now, capacities of both moving-totals are +//: no longer exceeded, so we are free to submit more units. We submit 7 +//: units, causing both 'Lp' and 'Ls' to be exceeded. +//: +//: o At time 't0 + 4s', the moving-totals are again updated. The 'Lp' limit +//: is no longer exceeded. The number of units held by the moving-total +//: tracking sustained rate matches the moving-total's capacity, and this +//: boundary condition imply and no units can be submitted, because +//: submitting any amount of units would cause 'Ls' to be exceeded. +//: +//: o At time 't0 + 6s', the moving-totals are again updated. Both 'Lp' and +//: 'Ls' are no longer exceeded. We submit 2 units. The 'Lp' limit is not +//: exceeded, but 'Ls' limit is exceeded. +// +///Monitoring Resource Usage +///------------------------- +// A 'balb::LeakyBucket' provides methods to both submit units and reserve +// units for future submission. Submitting a unit indicates that it has been +// consumed by the entity being modeled, and it is added to the moving-totals +// tracking both peak and sustained resource usage. +// +// Reserving a unit guarantees that available capacity will be reserved so that +// unit can be submitted in the future without exceeding the configured limits. +// Reserved units may be later submitted using the 'submitReserved' method or +// canceled using the 'cancelReserved' method. Reserved units permanently +// reside in the two moving-totals of consumed units, resulting in the +// reduction in the effective capacities of the moving-totals, until the +// reserved units are canceled or submitted. Reserving units effectively +// shortens the time-window during which the average sustained and peak rate +// are enforced. Therefore, the time interval between reserving units and +// submitting or canceling them should be kept as short as possible. For a +// practical example of using reserved units, please see +// 'balb_reservationguard'. +// +// The recommended usage of a rate limiter is to first check whether 1 unit can +// be added without exceeding the rate limiter's configured limits, and if so, +// consume the desired amount of the resource. Afterwards, submit the amount +// of consumed resource to the rate limiter. +// +// Whether submitting more units would exceed the configured limits can be +// determined using the 'wouldExceedBandwidth' method. The estimated amount of +// time to wait before 1 more unit will be allowed to be submitted can be +// determined using the 'calculateTimeToSubmit' method. +// +///Time Synchronization +///-------------------- +// rate limiter does not utilize an internal timer, so timing must be handled +// manually. Clients can specify an initial time interval for a rate limiter +// object at construction or using the 'reset' method. Whenever the state of a +// rate limiter object needs to be updated, clients must invoke the +// 'updateState' method specifying the current time interval. Since rate +// limiter cares only about the elapsed time (not absolute time), the specified +// time intervals may be relative to any arbitrary time origin, though all of +// them must refer to the same origin. For the sake of consistency, clients +// are encouraged to use the unix epoch time (such as the values returned by +// 'bdlt::CurrentTime::now'). +// +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Controlling Network Traffic Generation +///------------------------------------------------- +// Suppose that we want to send data over a network interface with the load +// spike limitations explained below: +// +//: o The long term average rate of resource usage (i.e., the sustained rate) +//: should not exceed 1024 bytes/s ('Rs'). +//: +//: o The period over which to monitor the long term average rate (i.e., the +//: sustained-rate time-window) should be 0.5s ('Wp'). +//: +//: o The peak resource usage (i.e., the peak rate) should not exceed 2048 +//: bytes/s ('Rp'). +//: +//: o The period over which to monitor the peak resource usage should be +//: 0.0625s (Wp). +// +// This is shown in Figure 2 below. +//.. +// Fig. 2: +// +// ^ Rate (Units per second) +// | _____ . +// | / B \ . +// 2048|---------------------------/-------\--------Rp (Maximum peak rate) +// | __ / \ . +// | / \ / A2 \ . +// | / A1 \ / \ . +// 1024|--------/------\ ------/---------------\----Rs (Maximum sustained rate) +// | __ / \ / \__. +// |__/ \/ \___/ . +// | . +// ---------------------------------------------> +// T (seconds) +//.. +// Notice that we can understand the limitations imposed by the rate-limiter +// graphically as the maximum area above the respective lines, 'Rp' and 'Rs', +// that the usage curve to allowed to achieve. In the example above: +// +// o The area above the sustained rate 'Rs' (e.g., 'A1' or 'A2+B') should +// contain no more than 512 bytes (Rs * Ws). +// +// o The area above the peak rate 'Rp' should contain no more than 128 bytes +// (Rp * Wp). +// +// Further suppose that we have a function, 'sendData', that transmits a +// specified amount of data over that network: +//.. +// bool sendData(size_t dataSize); +// // Send a specified 'dataSize' amount of data over the network. +// // Return 'true' if data was sent successfully and 'false' otherwise. +//.. +// First, we create a 'balb::RateLimiter' object having a sustained rate of +// 1024 bytes/s, a sustained-rate time-window of 0.5s +// (512 bytes / 1024 bytes/s), a peak-rate of 2048 bytes/s, and a peak-rate +// time-window of 0.0625s (128 bytes / 2048 bytes/s): +//.. +// bsls::Types::Uint64 sustainedRateLimit = 1024; +// bsls::TimeInterval sustainedRateWindow(0.5); +// bsls::Types::Uint64 peakRateLimit = 2048; +// bsls::TimeInterval peakRateWindow(0.0625); +// bsls::TimeInterval now = bdlt::CurrentTime::now(); +// +// balb::RateLimiter rateLimiter(sustainedRateLimit, +// sustainedRateWindow, +// peakRateLimit, +// peakRateWindow, +// now); +//.. +// Note that the rate limiter does not prevent the rate at any instant from +// exceeding either the peak-rate or the sustained rate; instead, it prevents +// the average rate over the peak-rate time-window from exceeding maximum +// peak-rate and the average rate over the sustained-rate time-window from +// exceeding the maximum sustained-rate. +// +// Then, we define the size of data to be send, the size of each data chunk, +// and a counter of data actually sent: +//.. +// bsls::Types::Uint64 sizeOfData = 10 * 1024; // in bytes +// bsls::Types::Uint64 chunkSize = 64; // in bytes +// bsls::Types::Uint64 bytesSent = 0; +//.. +// Now, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would exceed the rate limiter's bandwidth +// limits. If not, we send an additional chunk of data and submit the number +// of bytes sent to the leaky bucket. Note that 'submit' is invoked only after +// the data has been sent. +//.. +// while (bytesSent < sizeOfData) { +// now = bdlt::CurrentTime::now(); +// if (!rateLimiter.wouldExceedBandwidth(now)) { +// if (true == sendData(chunkSize)) { +// rateLimiter.submit(chunkSize); +// bytesSent += chunkSize; +// } +// } +//.. +// Finally, if submitting another byte will cause the rate limiter to exceed +// its bandwidth limits, then we wait until the submission will be allowed by +// waiting for an amount time returned by the 'calculateTimeToSubmit' method: +//.. +// else { +// bsls::TimeInterval timeToSubmit = +// rateLimiter.calculateTimeToSubmit(now); +// bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + +// (timeToSubmit.nanoseconds() % 1000 ? 1 : 0); +// bslmt::ThreadUtil::microSleep(static_cast(uS)); +// } +// } +//.. +// Notice that we wait by putting the thread into a sleep state instead of +// using busy-waiting to better optimize for multi-threaded applications. + +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +namespace BloombergLP { +namespace balb { + + //================== + // class RateLimiter + //================== + +class RateLimiter { + // This mechanism implements a rate limiter that allows clients to monitor + // and control the usage of a resource such that the rate of consumption + // stays within configured limits. The behavior of a rate limiter is + // determined by four properties: the sustained rate (in units/s), the + // sustained-rate time-window (in seconds), the peak rate (in units/s), and + // the peak-rate time-window (in seconds). All of these properties can be + // specified at construction or using the 'setRateLimits' method. + // + // Units can be indicated to a rate limiter as consumed by either + // submitting them using the 'submit' method. Units can be marked as + // reserved, which effectively shorten the sustained-rate time-window and + // the peak-rate time-window, by using the 'reserve' method. + // + // Whether submitting 1 more unit would exceed the configured limits can be + // determined using the 'wouldExceedBandwidth' method. The estimated + // amount of time to wait before 1 more unit will be allowed to be + // submitted can be determined using the 'calculateTimeToSubmit' method. + // + // The state of a rate limiter must be updated manually using the + // 'updateState' method supplying the current time interval. The time + // intervals supplied should all refer to the same time origin. + // + // A rate limiter keeps some statistics, including the number of submitted + // units, that can be accessed using the 'getStatistics' and reset using + // the 'resetStatistics' method. + // + // This class: + //: o is *exception* *neutral* (agnostic) + //: o is *const* *thread-safe* + // For terminology see 'bsldoc_glossary'. + + // DATA + LeakyBucket d_peakRateBucket; // 'balb::LeakyBucket' object for + // handling peak load + + LeakyBucket d_sustainedRateBucket; // 'balb::LeakyBucket' object for + // handling sustained load + + private: + // NOT IMPLEMENTED + RateLimiter& operator=(const RateLimiter&); + RateLimiter(const RateLimiter&); + + public: + // CREATORS + RateLimiter(bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow, + const bsls::TimeInterval& currentTime); + // Create a RateLimiter object, having the specified + // 'sustainedRateLimit', the specified 'sustainedRateWindow', the + // specified 'peakRateLimit', the specified 'peakRateWindow', and using + // the specified 'currentTime' as the initial 'lastUpdateTime'. The + // behavior is undefined unless '0 < sustainedRateLimit', + // '0 < sustainedRateWindow', '0 < peakRateLimit', + // '0 < peakRateWindow', the product of 'sustainedRateLimit' and + // 'sustainedRateWindow' can be represented by 64-bit unsigned integral + // type, and the product of 'peakRateLimit' and 'peakRateWindow' can be + // represented by 64-bit unsigned integral type. + + ~RateLimiter(); + // Destroy this object. + + // CLASS METHODS + static bool + supportsRateLimitsExactly(bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow); + // Returns 'true' if, supposing the specified 'sustainedRateLimit', + // 'sustainedRateWindow', 'peakRateLimit', and 'peakRateWindow' are + // used to initialize a 'RateLimiter' object, the corresponding query + // methods return the same values. The implementation of 'RateLimiter' + // uses 'balb::LeakyBucket' objects, and for some combinations of + // values the capacity of the 'balb::LeakyBucket' is rounded such that + // the rederived values differ. Note that this method is most likely + // to return 'true' when the product of each corresponding pair of + // limit and window (as a fraction of a second) is integral. + + // MANIPULATORS + bsls::TimeInterval calculateTimeToSubmit( + const bsls::TimeInterval& currentTime); + // Update the state of this rate limiter to the specified + // 'currentTime'. Return the estimated time interval that should pass + // from 'currentTime' until 1 more unit can be submitted to this rate + // limiter without exceeding its configured limits. The number of + // nanoseconds in the returned time interval is rounded up. Note that + // a time interval of 0 is returned if 1 or more units can be submitted + // at 'currentTime'. Also note that after waiting for the returned + // time interval, clients should typically check again using this + // method, because additional units may have been submitted in the + // interim. + + void cancelReserved(bsls::Types::Uint64 numUnits); + // Cancel the specified 'numUnits' that were previously reserved. The + // behavior is undefined unless 'numUnits <= unitsReserved()'. + + void reserve(bsls::Types::Uint64 numUnits); + // Reserve the specified 'numUnits' for future use by this rate + // limiter. The behavior is undefined unless the sum of 'numUnits', + // unused units previously submitted to this rate limiter, and + // 'unitsReserved' can be represented by a 64-bit unsigned integral + // type. + + void reset(const bsls::TimeInterval& currentTime); + // Reset the statistics counter for this rate limiter to 0, and set the + // 'lastUpdateTime' of this rate limiter to the specified + // 'currentTime'. + + void resetStatistics(); + // Reset the statics collected for this rate limiter by setting the + // number of units used and the number of units submitted to 0, and set + // the 'statisticsCollectionStartTime' to the 'lastUpdateTime' of this + // leaky bucket. + + void setRateLimits(bsls::Types::Uint64 sustainedRateLimit, + const bsls::TimeInterval& sustainedRateWindow, + bsls::Types::Uint64 peakRateLimit, + const bsls::TimeInterval& peakRateWindow); + // Set the sustained rate of this rate limiter to the specified + // 'sustainedRateLimit', the sustained-rate time-window to the + // specified 'sustainedRateWindow', the peak rate to the specified + // 'peakRateLimit' and the peak-rate time-window to the specified + // 'peakRateWindow'. The behavior is undefined unless + // '0 < sustainedRateLimit', '0 < sustainedRateWindow', + // '0 < peakRateLimit', '0 < peakRateWindow', the product of + // 'sustainedRateLimit' and 'sustainedRateWindow' can be represented by + // 64-bit unsigned integral type, and the product of 'peakRateLimit' + // and 'peakRateWindow' can be represented by 64-bit unsigned integral + // type. + + void submit(bsls::Types::Uint64 numUnits); + // Submit the specified 'numUnits' to this rate limiter. The behavior + // is undefined unless the sum of 'numUnits', unused units previously + // submitted to this rate limiter, and 'unitsReserved' can be + // represented by a 64-bit unsigned integral type. + + void submitReserved(bsls::Types::Uint64 numUnits); + // Submit the specified 'numUnits' that were previously reserved. The + // behavior is undefined unless 'numUnits <= unitsReserved()'. + + void updateState(const bsls::TimeInterval& currentTime); + // Set the 'lastUpdateTime' of this rate limiter to the specified + // 'currentTime'. If the 'currentTime' is after 'lastUpdateTime', then + // recalculate number of units available for consumption based on the + // 'peakRate', 'sustainedRate' and the time interval between + // 'lastUpdateTime' and 'currentTime'. If 'currentTime' is before + // 'statisticsCollectionStartTime', set it' to 'currentTime'. + + bool wouldExceedBandwidth(const bsls::TimeInterval& currentTime); + // Update the state of this rate limiter to the specified + // 'currentTime'. Return 'true' if submitting 1 unit at the + // 'currentTime' would exceed the configured limits, and false + // otherwise. + + // ACCESSORS + void getStatistics(bsls::Types::Uint64* submittedUnits, + bsls::Types::Uint64* unusedUnits) const; + // Load, into the specified 'submittedUnits' and the specified + // 'unusedUnits' respectively, the numbers of submitted units and the + // number of unused units for this rate limiter from the + // 'statisticsCollectionStartTime' to the 'lastUpdateTime. The number + // of unused units is the difference between the number of units that + // could have been consumed at the sustained rate and the number of + // units actually submitted for the time period. + + bsls::TimeInterval lastUpdateTime() const; + // Return the time when this rate limiter was last updated. + + bsls::Types::Uint64 peakRateLimit() const; + // Return the peak rate of this rate limiter. + + bsls::TimeInterval peakRateWindow() const; + // Return the peak-rate time-period of this rate limiter. Note that + // this period is generally significantly shorter than + // 'sustainedRateWindow'. + + bsls::TimeInterval statisticsCollectionStartTime() const; + // Return the time interval when the collection of the statistics (as + // returned by 'getStatistics') started. + + bsls::Types::Uint64 sustainedRateLimit() const; + // Return the sustained rate of this rate limiter. + + bsls::TimeInterval sustainedRateWindow() const; + // Return the sustained-rate time-period of this rate limiter. Note + // that this period is generally significantly longer than the + // 'peakRateWindow'. + + bsls::Types::Uint64 unitsReserved() const; + // Return the number of reserved units for this rate limiter. +}; + +// ============================================================================ +// INLINE FUNCTION DEFINITIONS +// ============================================================================ + + //------------------ + // class RateLimiter + //------------------ + +// MANIPULATORS +inline +void RateLimiter::cancelReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT(numUnits <= unitsReserved()); + + d_peakRateBucket.cancelReserved(numUnits); + d_sustainedRateBucket.cancelReserved(numUnits); +} + +inline +void RateLimiter::reserve(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= ULLONG_MAX - unitsReserved()); + + BSLS_ASSERT_SAFE(d_sustainedRateBucket.unitsInBucket() <= + ULLONG_MAX - unitsReserved()- numUnits); + + BSLS_ASSERT_SAFE(d_peakRateBucket.unitsInBucket() <= + ULLONG_MAX - unitsReserved()- numUnits); + + d_peakRateBucket.reserve(numUnits); + d_sustainedRateBucket.reserve(numUnits); +} + +inline +void RateLimiter::reset(const bsls::TimeInterval& currentTime) +{ + d_peakRateBucket.reset(currentTime); + d_sustainedRateBucket.reset(currentTime); +} + +inline +void RateLimiter::resetStatistics() +{ + d_sustainedRateBucket.resetStatistics(); +} + +inline +void RateLimiter::submit(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= + ULLONG_MAX - d_sustainedRateBucket.unitsInBucket()); + + BSLS_ASSERT_SAFE(unitsReserved() <= + ULLONG_MAX - d_sustainedRateBucket.unitsInBucket()- numUnits); + + BSLS_ASSERT_SAFE(numUnits <= + ULLONG_MAX - d_peakRateBucket.unitsInBucket()); + + BSLS_ASSERT_SAFE(unitsReserved() <= + ULLONG_MAX - d_peakRateBucket.unitsInBucket()- numUnits); + + d_peakRateBucket.submit(numUnits); + d_sustainedRateBucket.submit(numUnits); +} + +inline +void RateLimiter::submitReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT(numUnits <= unitsReserved()); + + // There is no need to check whether 'numUnits' causes overflow because the + // reserved units was already checked by the 'reserve' method. + + d_peakRateBucket.submitReserved(numUnits); + d_sustainedRateBucket.submitReserved(numUnits); +} + +inline +void RateLimiter::updateState(const bsls::TimeInterval& currentTime) +{ + d_peakRateBucket.updateState(currentTime); + d_sustainedRateBucket.updateState(currentTime); +} + +inline +bool RateLimiter::wouldExceedBandwidth(const bsls::TimeInterval& currentTime) +{ + return (d_peakRateBucket.wouldOverflow(currentTime) || + d_sustainedRateBucket.wouldOverflow(currentTime)); +} + +// ACCESSORS +inline +void RateLimiter::getStatistics(bsls::Types::Uint64* submittedUnits, + bsls::Types::Uint64* unusedUnits) const +{ + BSLS_ASSERT_SAFE(0 != submittedUnits); + BSLS_ASSERT_SAFE(0 != unusedUnits); + + // The statistics is collected from the sustained rate leaky bucket. + + d_sustainedRateBucket.getStatistics(submittedUnits, unusedUnits); +} + +inline +bsls::TimeInterval RateLimiter::lastUpdateTime() const +{ + return bsl::max(d_sustainedRateBucket.lastUpdateTime(), + d_peakRateBucket.lastUpdateTime()); +} + +inline +bsls::Types::Uint64 RateLimiter::peakRateLimit() const +{ + return d_peakRateBucket.drainRate(); +} + +inline +bsls::TimeInterval RateLimiter::peakRateWindow() const +{ + return LeakyBucket::calculateTimeWindow(d_peakRateBucket.drainRate(), + d_peakRateBucket.capacity()); +} + +inline +bsls::TimeInterval RateLimiter::statisticsCollectionStartTime() const +{ + return d_sustainedRateBucket.statisticsCollectionStartTime(); +} + +inline +bsls::Types::Uint64 RateLimiter::sustainedRateLimit() const +{ + return d_sustainedRateBucket.drainRate(); +} + +inline +bsls::TimeInterval RateLimiter::sustainedRateWindow() const +{ + return LeakyBucket::calculateTimeWindow(d_sustainedRateBucket.drainRate(), + d_sustainedRateBucket.capacity()); +} + +inline +bsls::Types::Uint64 RateLimiter::unitsReserved() const +{ + BSLS_ASSERT_SAFE(d_sustainedRateBucket.unitsReserved() == + d_peakRateBucket.unitsReserved()); + + return d_sustainedRateBucket.unitsReserved(); +} + +} // close package namespace +} // close enterprise namespace + +#endif + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_ratelimiter.t.cpp b/groups/bal/balb/balb_ratelimiter.t.cpp new file mode 100644 index 0000000000..93a7cdf581 --- /dev/null +++ b/groups/bal/balb/balb_ratelimiter.t.cpp @@ -0,0 +1,1640 @@ +//balb_ratelimiter.t.cpp -*-C++-*- + +#include + +#include + +#include + +#include + +#include +#include + +using namespace BloombergLP; +using namespace bsl; + +//============================================================================= +// TEST PLAN +//----------------------------------------------------------------------------- +// Overview +// -------- +// The component under test implements a mechanism. +// +// Class Methods: +//: o 'supportsRateLimitsExactly' +// +// Primary Manipulators: +//: o 'setRateLimits' +// +// Basic Accessors: +//: o 'peakRateLimit' +//: o 'sustainedRateLimit' +//: o 'peakRateWindow' +//: o 'sustainedRateWindow' +//: o 'lastUpdateTime' +//: o 'unitsReserved' +// +// This class also provides a value constructor capable of creating an object +// having any parameters. +// +// Global Concerns: +//: o ACCESSOR methods are declared 'const'. +//: o CREATOR & MANIPULATOR pointer/reference parameters are declared 'const'. +//: o Precondition violations are detected in appropriate build modes. +// +// Global Assumptions: +//: o ACCESSOR methods are 'const' thread-safe. +//----------------------------------------------------------------------------- +// +// CREATORS +// [ 2] RateLimiter(sR, sRW, pR, pRW, currentTime); +// +// MANIPULATORS +// [ 6] bool supportsRateLimitsExactly(sR, sRW, pR, pRW); +// [ 6] void setRateLimits(sR, sRW, pR, pRW, currentTime); +// [ 4] void submit(bsls::Types::Uint64 numOfUnits); +// [ 5] void reserve(bsls::Types::Uint64 numOfUnits); +// [ 8] void updateState(const bsls::TimeInterval& currentTime); +// [ 4] bool wouldExceedBandwidth(currentTime); +// [ 5] void submitReserved(bsls::Types::Unit64 numOfUnits); +// [12] void cancelReserved(bsls::Types::Unit64 numOfUnits); +// [10] void resetStatistics(); +// [11] void reset(const bsls::TimeInterval& currentTime); +// [ 7] bsls::TimeInterval calculateTimeToSubmit(currentTime); +// +// ACCESSORS +// [ 3] bsls::Types::Uint64 peakRateLimit() const; +// [ 3] bsls::Types::Uint64 sustainedRateLimit() const; +// [ 3] bsls::TimeInterval peakRateWindow() const; +// [ 3] bsls::TimeInterval sustainedRateWindow() const; +// [ 5] bsls::Types::Uint64 unitsReserved() const; +// [ 3] bsls::TimeInterval lastUpdateTime() const; +// [ 9] void getStatistics(*submittedUnits, *unusedUnits) const; +// [ 3] bsls::TimeInterval statisticsCollectionStartTime() const; +// +//----------------------------------------------------------------------------- +// [ 1] BREATHING TEST +// [13] USAGE EXAMPLE +// [ 3] All accessor methods are declared 'const'. +// [ *] All creator/manipulator ptr./ref. parameters are 'const'. +//============================================================================= + +//============================================================================= +// STANDARD BDE ASSERT TEST MACRO +//----------------------------------------------------------------------------- +static int testStatus = 0; + +static void aSsErT(int c, const char *s, int i) +{ + if (c) { + cout << "Error " << __FILE__ << "(" << i << "): " << s + << " (failed)" << endl; + if (testStatus >= 0 && testStatus <= 100) ++testStatus; + } +} + +# define ASSERT(X) { aSsErT(!(X), #X, __LINE__); } +//----------------------------------------------------------------------------- +#define LOOP_ASSERT(I,X) { \ + if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__); }} + +#define LOOP2_ASSERT(I,J,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \ + << J << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP3_ASSERT(I,J,K,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \ + << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP4_ASSERT(I,J,K,L,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \ + #K << ": " << K << "\t" << #L << ": " << L << "\n"; \ + aSsErT(1, #X, __LINE__); } } + +#define LOOP0_ASSERT ASSERT +#define LOOP1_ASSERT LOOP_ASSERT + +//============================================================================= +// STANDARD BDE VARIADIC ASSERT TEST MACROS +//----------------------------------------------------------------------------- + +#define NUM_ARGS_IMPL(X5, X4, X3, X2, X1, X0, N, ...) N +#define NUM_ARGS(...) NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0, "") + +#define LOOPN_ASSERT_IMPL(N, ...) LOOP ## N ## _ASSERT(__VA_ARGS__) +#define LOOPN_ASSERT(N, ...) LOOPN_ASSERT_IMPL(N, __VA_ARGS__) + +#define ASSERTV(...) LOOPN_ASSERT(NUM_ARGS(__VA_ARGS__), __VA_ARGS__) + +// ============================================================================ +// SEMI-STANDARD TEST OUTPUT MACROS +// ---------------------------------------------------------------------------- + +#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value. +#define Q(X) cout << "<| " #X " |>" << endl; // Quote identifier literally. +#define P_(X) cout << #X " = " << (X) << ", " << flush; // 'P(X)' without '\n' +#define T_ cout << "\t" << flush; // Print tab w/o newline. +#define L_ __LINE__ // current Line number + +// ============================================================================ +// NEGATIVE-TEST MACRO ABBREVIATIONS +// ---------------------------------------------------------------------------- + +#define ASSERT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_PASS(EXPR) +#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR) +#define ASSERT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL(EXPR) +#define ASSERT_OPT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR) + +//============================================================================= +// GLOBAL TYPEDEFS/CONSTANTS FOR TESTING +//----------------------------------------------------------------------------- +typedef balb::RateLimiter Obj; +typedef bsls::TimeInterval Ti; +typedef bsls::Types::Uint64 Uint64; +typedef unsigned int uint; + +//============================================================================= +// USAGE EXAMPLE +//----------------------------------------------------------------------------- + +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Controlling Network Traffic Generation +///------------------------------------------------- +// Suppose that we want to send data over a network interface with the load +// spike limitations explained below: +// +//: o The long term average rate of resource usage (i.e., the sustained rate) +//: should not exceed 1024 bytes/s ('Rs'). +//: +//: o The period over which to monitor the long term average rate (i.e., the +//: sustained-rate time-window) should be 0.5s ('Wp'). +//: +//: o The peak resource usage (i.e., the peak rate) should not exceed 2048 +//: bytes/s ('Rp'). +//: +//: o The period over which to monitor the peak resource usage should be +//: 0.0625s (Wp). +// +// This is shown in Figure 2 below. +//.. +// Fig. 2: +// +// ^ Rate (Units per second) +// | _____ . +// | / B \ . +// 2048|---------------------------/-------\--------Rp (Maximum peak rate) +// | __ / \ . +// | / \ / A2 \ . +// | / A1 \ / \ . +// 1024|--------/------\ ------/---------------\----Rs (Maximum sustained rate) +// | __ / \ / \__. +// |__/ \/ \___/ . +// | . +// ---------------------------------------------> +// T (seconds) +//.. +// Notice that we can understand the limitations imposed by the rate-limiter +// graphically as the maximum area above the respective lines, 'Rp' and 'Rs', +// that the usage curve to allowed to achieve. In the example above: +// +// o The area above the sustained rate 'Rs' (e.g., 'A1' or 'A2+B') should +// contain no more than 512 bytes. +// +// o The area above the peak rate 'Rp' should contain no more than 128 bytes. +// +// Further suppose that we have a function, 'sendData', that transmits a +// specified amount of data over that network: +//.. +bool sendData(size_t dataSize) + // Send a specified 'dataSize' amount of data over the network. + // Return 'true' if data was sent successfully and 'false' otherwise. +{ + (void)(dataSize); +//.. +// For simplicity, 'sendData' will not actually send any data and will always +// return 'true'. +//.. + return true; +} +//.. + +//============================================================================= +// MAIN PROGRAM +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int test = argc > 1 ? atoi(argv[1]) : 0; + bool verbose = argc > 2; + //bool veryVerbose = argc > 3; + //bool veryVeryVerbose = argc > 4; + + cout << "TEST " << __FILE__ << " CASE " << test << endl; + + switch (test) { case 0: // Zero is always the leading case. + case 13: { + // -------------------------------------------------------------------- + // USAGE EXAMPLE + // The usage example provided in the component header file must + // compile, link, and run on all platforms as shown. + // + // Plan: + // Incorporate usage example from header into driver, remove leading + // comment characters and replace 'assert' with 'ASSERT'. + // + // Testing: + // USAGE EXAMPLE + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TESTING USAGE EXAMPLE" << endl + << "=====================" << endl; + +// First, we create a 'RateLimiter' object having a sustained rate of 1024 +// bytes/s, a sustained-rate time-window of 0.5s (512 bytes / 1024 bytes/s), +// a peak-rate of 2048 bytes/s, and a peak-rate time-window of 0.0625s +// (128 bytes / 2048 bytes/s): +//.. + bsls::Types::Uint64 sustainedRateLimit = 1024; + bsls::TimeInterval sustainedRateWindow(0.5); + bsls::Types::Uint64 peakRateLimit = 2048; + bsls::TimeInterval peakRateWindow(0.0625); + bsls::TimeInterval now = bdlt::CurrentTime::now(); + + balb::RateLimiter rateLimiter(sustainedRateLimit, + sustainedRateWindow, + peakRateLimit, + peakRateWindow, + now); +//.. +// Note that the rate limiter does not prevent the rate at any instant from +// exceeding either the peak-rate or the sustained rate; instead, it prevents +// the average rate over the peak-rate time-window from exceeding maximum +// peak-rate and the average rate over the sustained-rate time-window from +// exceeding the maximum sustained-rate. +// +// Then, we define the size of data to be send, the size of each data chunk, +// and a counter of data actually sent: +//.. + bsls::Types::Uint64 sizeOfData = 10 * 1024; // in bytes + bsls::Types::Uint64 chunkSize = 64; // in bytes + bsls::Types::Uint64 bytesSent = 0; +//.. +// Now, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would exceed the rate limiter's bandwidth +// limits. If not, we send an additional chunk of data and submit the number +// of bytes sent to the leaky bucket. Note that 'submit' is invoked only after +// the data has been sent. +//.. + while (bytesSent < sizeOfData) { + now = bdlt::CurrentTime::now(); + if (!rateLimiter.wouldExceedBandwidth(now)) { + if (true == sendData(chunkSize)) { + rateLimiter.submit(chunkSize); + bytesSent += chunkSize; + } + } +//.. +// Finally, if submitting another byte will cause the rate limiter to exceed +// its bandwidth limits, then we wait until the submission will be allowed by +// waiting for an amount time returned by the 'calculateTimeToSubmit' method: +//.. + else { + bsls::TimeInterval timeToSubmit = + rateLimiter.calculateTimeToSubmit(now); + bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + + (timeToSubmit.nanoseconds() % 1000 ? 1 : 0); + bslmt::ThreadUtil::microSleep(static_cast(uS)); + } + } +//.. +// Notice that we wait by putting the thread into a sleep state instead of +// using busy-waiting to better optimize for multi-threaded applications. +//.. + } break; + case 12: { + // -------------------------------------------------------------------- + // CLASS METHOD 'cancelReserved' + // + // Concerns: + //: 1 The method decrements the number of 'unitsReserved'. + //: + //: 2 The method does not submit units. + //: + //: 3 The method correctly handles the case, when 'numOfUnits' is + //: greater than the number of 'unitsReserved'. + // + // Testing: + // void cancelReserved(bsls::Types::Uint64 numOfUnits) + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "CLASS METHOD 'cancelReserved'" << endl + << "=============================" << endl; + + const Uint64 S_RATE = 1000; + const Ti S_WND = Ti(1); + const Uint64 P_RATE = 10000; + const Ti P_WND = Ti(0.01); + + struct { + int d_line; + Uint64 d_unitsToReserve; + Uint64 d_unitsToCancel; + Uint64 d_expectedUnitsReserved; + } DATA[] = { + + // LINE RESERVE CANCEL EXP_RES + // ---- ---------- ------------- -------------------- + {L_, 1000, 300, 700}, + // C-3 + {L_, 1000, 1000, 0}, + {L_, 1000, 700, 300}, + {L_, ULLONG_MAX, ULLONG_MAX/2, ULLONG_MAX-ULLONG_MAX/2} + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti=0; ti= 9 && p5 >= 9 && s2 >= 9 && s5 >= 9)); + LOOP_ASSERT(LINE, EXACT == x.supportsRateLimitsExactly( + S_RATE, S_WND, P_RATE, P_WND)); + + x.setRateLimits(S_RATE, S_WND, P_RATE, P_WND); + const Obj& X = x; + + // C-1, C-2 + LOOP_ASSERT(LINE, S_RATE == X.sustainedRateLimit()); + LOOP_ASSERT(LINE, EXP_S_WND == X.sustainedRateWindow()); + LOOP_ASSERT(LINE, P_RATE == X.peakRateLimit()); + LOOP_ASSERT(LINE, EXP_P_WND == X.peakRateWindow()); + LOOP_ASSERT(LINE, CREATION_TIME == X.lastUpdateTime()); + + LOOP_ASSERT(LINE, + CREATION_TIME == X.statisticsCollectionStartTime()); + } + + // C-3 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + // Zero rate or zero window. + ASSERT(!x.supportsRateLimitsExactly(0, Ti(15), 10, Ti(10))); + ASSERT_FAIL( x.setRateLimits(0, Ti(15), 10, Ti(10))); + ASSERT(!x.supportsRateLimitsExactly(1, Ti( 0), 10, Ti(10))); + ASSERT_FAIL( x.setRateLimits(1, Ti( 0), 10, Ti(10))); + ASSERT(!x.supportsRateLimitsExactly(1, Ti(15), 0, Ti(10))); + ASSERT_FAIL( x.setRateLimits(1, Ti(15), 0, Ti(10))); + ASSERT(!x.supportsRateLimitsExactly(1, Ti(15), 10, Ti( 0))); + ASSERT_FAIL( x.setRateLimits(1, Ti(15), 10, Ti( 0))); + + // Negative window. + ASSERT(!x.supportsRateLimitsExactly(1, Ti(-15), 10, Ti(10))); + ASSERT_FAIL( x.setRateLimits(1, Ti(-15), 10, Ti(10))); + ASSERT(!x.supportsRateLimitsExactly(1, Ti(15), 10, Ti(-10))); + ASSERT_FAIL( x.setRateLimits(1, Ti(15), 10, Ti(-10))); + } + } break; + case 5: { + // -------------------------------------------------------------------- + // CLASS METHOD 'reserve', 'unitsReserved', 'submitReserved' + // + // Concerns: + //: 1 'reserve' increments 'unitsReserved'. + //: + //: 2 'unitsReserved' returns number of units currently reserved. + //: + //: 3 'submitReserved' decrements 'unitsReserved' and submits units. + //: + //: 4 'submitReserved' may submit more units, than there are actually + //: reserved. + //: + //: 5 Specifying wrong parameters to 'reserve' causes certain behavior + //: in special build configuration. + //: + //: 6 Specifying wrong parameters to 'submitReserved' causes certain + //: behavior in special build configuration. + // + // Testing: + // void reserve(bsls::Types::Uint64 numOfUnits); + // bsls::Types::Uint64 unitsReserved(); + // void submitReserved(bsls::Types::Uint64 numOfUnits); + //--------------------------------------------------------------------- + + if (verbose) + cout << endl + << "TESTING: 'reserve', 'unitsReserved', 'submitReserved'" + << endl + << "=====================================================" + << endl; + + const Uint64 S_RATE = 1000; + const Ti S_WND = Ti(1); + const Uint64 P_RATE = 10000; + const Ti P_WND = Ti(0.01); + + struct { + int d_line; + Uint64 d_unitsToReserve; + Uint64 d_unitsToSubmit; + Uint64 d_expectedUnitsReserved; + } DATA[] = { + + // LINE RESERVE SUBMIT EXP_RES + // ---- ---------- ------------- -------------------- + {L_, 1000, 300, 700}, + // C-4 + {L_, 1000, 1000, 0}, + {L_, 1000, 700, 300}, + {L_, ULLONG_MAX, ULLONG_MAX/2, ULLONG_MAX-ULLONG_MAX/2} + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for(int ti=0; ti= t1); + } + } + } + + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + // Units reserved, trying to submit & reserve. + + Obj y1(100, Ti(10), 1000, Ti(1), Ti(0)); + + // C-5 + y1.reserve(ULLONG_MAX); + ASSERT_SAFE_FAIL(y1.submit(1)); + ASSERT_SAFE_FAIL(y1.reserve(1)); + + Obj y2(100, Ti(10), 1000, Ti(1), Ti(0)); + + y2.reserve(ULLONG_MAX/2); + ASSERT_SAFE_FAIL(y2.submit((ULLONG_MAX>>2)*3)); + ASSERT_SAFE_FAIL(y2.reserve((ULLONG_MAX>>2)*3)); + + // C-6 + Obj y3(100, Ti(10), 1000, Ti(1), Ti(0)); + y3.submit(1); + y3.reserve(1); + ASSERT_FAIL(y3.submitReserved(ULLONG_MAX)); + + ASSERT_FAIL(y3.submitReserved(ULLONG_MAX/2+1)); + } + + } break; + case 4: { + // -------------------------------------------------------------------- + // 'submit', 'wouldExceedBandwidth', 'calculateTimeToSubmit' + // + // Concerns: + //: 1 'wouldExceedBandwidth' has the same behavior, when compared to + //: the pair of 'balb::LeakyBucket' objects. + //: + //: 2 Specifying wrong parameters to 'submit' causes certain behavior + //: in special build configuration. + // + // Testing: + // void submit(unsigned int numOfUnits); + // bool wouldExceedBandwidth(bsls::TimeInterval currentTime); + // -------------------------------------------------------------------- + + if (verbose) + cout << endl + << "'submit', 'wouldExceedBandwidth', calculateTimeToSubmit'" + << endl + << "========================================================" + << endl; + + struct { + int d_line; + Uint64 d_sustRate; + Ti d_sustWindow; + Uint64 d_peakRate; + Ti d_peakWindow; + Ti d_creationTime; + Uint64 d_nSubmits; + Uint64 d_units; + Ti d_submitInterval; + } DATA[] = { + + // LINE S_RATE S_WND P_RATE P_WND TCREATE NSUBMITS UNITS INTERVAL + // ---- ------- ------- ------- --------- -------- -------- ------ -------- + {L_, 100, Ti(10), 1000, Ti( 1), Ti(0), 500, 10, Ti( 0.1)}, + {L_, 100, Ti(10), 1000, Ti( 1), Ti(0), 50, 100, Ti( 0.1)}, + {L_, 100, Ti(10), 1000, Ti(0.5), Ti(0), 10, 100, Ti( 1)}, + {L_, 100, Ti(10), 1000, Ti(0.1), Ti(0), 10, 100, Ti(0.01)} + + }; + + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + if (verbose) cout << endl << "Testing: 'wouldExceedBandwidth'" + << endl; + + for (int ti = 0; ti < NUM_DATA; ++ti) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 S_RATE = DATA[ti].d_sustRate; + const Ti S_WND = DATA[ti].d_sustWindow; + const Uint64 P_RATE = DATA[ti].d_peakRate; + const Ti P_WND = DATA[ti].d_peakWindow; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Uint64 N_SUBMITS = DATA[ti].d_nSubmits; + const Uint64 UNITS = DATA[ti].d_units; + const Ti SUBMIT_INTERVAL = DATA[ti].d_submitInterval; + + const Uint64 S_CAP = balb::LeakyBucket::calculateCapacity(S_RATE, + S_WND); + + const Uint64 P_CAP = balb::LeakyBucket::calculateCapacity(P_RATE, + P_WND); + + balb::LeakyBucket peakLB(P_RATE, P_CAP, CREATION_TIME); + balb::LeakyBucket sustLB(S_RATE, S_CAP, CREATION_TIME); + + Obj x(S_RATE, S_WND, P_RATE, P_WND, CREATION_TIME); + + Ti curTime = CREATION_TIME; + + for(unsigned int i = 0; i < N_SUBMITS; i++) { + + curTime += SUBMIT_INTERVAL; + + peakLB.submit(UNITS); + sustLB.submit(UNITS); + + x.submit(UNITS); + + const bool LB_RESULT = peakLB.wouldOverflow(curTime) || + sustLB.wouldOverflow(curTime); + + LOOP_ASSERT(LINE, + LB_RESULT == x.wouldExceedBandwidth(curTime)); + + const Ti LB_TIMESTAMP = bsl::max(sustLB.lastUpdateTime(), + peakLB.lastUpdateTime()); + + LOOP_ASSERT(LINE, LB_TIMESTAMP == x.lastUpdateTime()); + } + } + + if (verbose) cout << endl + << "Testing: 'calculateTimeToSubmit'" << endl; + + for (int ti = 0; ti < NUM_DATA; ++ti) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 S_RATE = DATA[ti].d_sustRate; + const Ti S_WND = DATA[ti].d_sustWindow; + const Uint64 P_RATE = DATA[ti].d_peakRate; + const Ti P_WND = DATA[ti].d_peakWindow; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Uint64 N_SUBMITS = DATA[ti].d_nSubmits; + const Uint64 UNITS = DATA[ti].d_units; + const Ti SUBMIT_INTERVAL = DATA[ti].d_submitInterval; + + const Uint64 S_CAP = balb::LeakyBucket::calculateCapacity(S_RATE, + S_WND); + + const Uint64 P_CAP = balb::LeakyBucket::calculateCapacity(P_RATE, + P_WND); + + balb::LeakyBucket peakLB(P_RATE, P_CAP, CREATION_TIME); + balb::LeakyBucket sustLB(S_RATE, S_CAP, CREATION_TIME); + + Obj x(S_RATE, S_WND, P_RATE, P_WND, CREATION_TIME); + + Ti curTime = CREATION_TIME; + + for(unsigned int i = 0; i < N_SUBMITS; i++) { + + curTime += SUBMIT_INTERVAL; + + peakLB.submit(UNITS); + sustLB.submit(UNITS); + + x.submit(UNITS); + + const Ti LB_RESULT = bsl::max( + peakLB.calculateTimeToSubmit(curTime), + sustLB.calculateTimeToSubmit(curTime)); + + LOOP_ASSERT(LINE, + LB_RESULT == x.calculateTimeToSubmit(curTime)); + + const Ti LB_TIMESTAMP = bsl::max(sustLB.lastUpdateTime(), + peakLB.lastUpdateTime()); + + LOOP_ASSERT(LINE, LB_TIMESTAMP == x.lastUpdateTime()); + } + + const Ti CHECK_TIME = curTime + x.calculateTimeToSubmit(curTime); + + LOOP_ASSERT(LINE, false == x.wouldExceedBandwidth(CHECK_TIME)); + } + + // C-2 + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + // Units submitted, trying to submit & reserve. + + Obj x1(100, Ti(10), 1000, Ti(1), Ti(0)); + + x1.submit(ULLONG_MAX); + ASSERT_SAFE_FAIL(x1.submit(1)); + ASSERT_SAFE_FAIL(x1.reserve(1)); + + Obj x2(100, Ti(10), 1000, Ti(1), Ti(0)); + + x2.submit(ULLONG_MAX/2); + ASSERT_SAFE_FAIL(x2.submit((ULLONG_MAX>>2)*3)); + ASSERT_SAFE_FAIL(x2.reserve((ULLONG_MAX>>2)*3)); + + } + + } break; + case 3: { + // -------------------------------------------------------------------- + // BASIC ACCESSORS + // Ensure each basic accessor properly interprets object state. + // + // Concerns: + //: 1 Each accessor returns the value of the corresponding attribute + //: of the object. + //: + //: 2 Each accessor method is declared 'const'. + // + // Plan: + //: 1 Create an object using the value constructor. Verify that each + //: basic accessor, invoked on a reference providing non-modifiable + //: access to the object, returns the expected value. (C-2) + //: + //: 2 Create another object using the value constructor having a + //: different value for each attribute compared to a default + //: constructed object. Verify that each basic accessor, invoked on + //: a reference providing non-modifiable access to the object, + //: returns the expected value. (C-1) + // + // Testing: + // bsls::Types::Uint64 peakRateLimit() const; + // bsls::Types::Uint64 sustainedRateLimit() const; + // bsls::TimeInterval peakRateWindow() const; + // bsls::TimeInterval sustainedRateWindow() const; + // bsls::TimeInterval lastUpdateTime() const; + // bsls::TimeInterval statisticsCollectionStartTime() const; + //----------------------------------------------------------------- + + if (verbose) cout << endl + << "BASIC ACCESSORS" << endl + << "===============" << endl; + + Obj mX(1, Ti(10), 10, Ti(1), Ti(0)); const Obj& X = mX; + + ASSERT(10 == X.peakRateLimit()); + ASSERT(Ti(1) == X.peakRateWindow()); + ASSERT(1 == X.sustainedRateLimit()); + ASSERT(Ti(10) == X.sustainedRateWindow()); + ASSERT(Ti(0) == X.lastUpdateTime()); + ASSERT(Ti(0) == X.statisticsCollectionStartTime()); + ASSERT(0 == X.unitsReserved()); + + Uint64 SR = 100; + Ti SW(50); + Uint64 PR = 1000; + Ti PW(10); + Ti CT(6); + Uint64 RU = 22; + + Obj mY(SR, SW, PR, PW, CT); const Obj& Y = mY; + mY.reserve(RU); + ASSERT(PR == Y.peakRateLimit()); + ASSERT(PW == Y.peakRateWindow()); + ASSERT(SR == Y.sustainedRateLimit()); + ASSERT(SW == Y.sustainedRateWindow()); + ASSERT(CT == Y.lastUpdateTime()); + ASSERT(CT == Y.statisticsCollectionStartTime()); + ASSERT(RU == Y.unitsReserved()); + + } break; + case 2: { + // -------------------------------------------------------------------- + // VALUE CTOR + // Ensure that we can put an object into any initial state relevant + // for thorough testing. + // + // Concerns: + //: 1 The value constructor can create an object having any value that + //: does not violate the constructor's documented preconditions. + //: + //: 2 The capacity of a underlying leaky bucket is set to the + //: rounded-down product of 'sustainedRateWindow' and + //: 'sustainedRateLimit'. As a result, the 'sustainedRateLimit' + //: retrieved from an object's accessor may be one less than the + //: original specified value. + //: + //: 3 If less than one unit is transmitted during the specified + //: 'sustainedRateWindow' at 'sustainedRateLimit' then + //: 'sustainRateWindow' will be set to the time period during which 1 + //: unit is transmitted. + //: + //: 4 The capacity of a underlying leaky bucket is set to the + //: rounded-down product of 'peakRateWindow' and 'peakRateLimit'. As + //: a result, the 'peakRateLimit' retrieved from an object's accessor + //: may be one less than the original specified value. + //: + //: 5 If less than one unit is transmitted during the specified + //: 'peakRateWindow' at 'peakRateLimit' then 'sustainRateWindow' will + //: be set to the time period during which 1 unit is transmitted. + //: + //: 6 QoI: Assert preconditions violations are detected when enabled. + // + // Plan: + //: 2 Use a table driven test, for a set of varied possible attributes, + //: to invoke the value constructor and verify the values of the + //: resulting objects. (C-1..5) + //: + //: 3 Verify that, in appropriate build modes, defensive checks are + //: triggered for invalid attribute values, but not triggered for + //: adjacent valid ones (using the 'BSLS_ASSERTTEST_*' macros). + //: (C-6) + // + // Testing: + // RateLimiter(sR, sRW, pR, pRW, currentTime); + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "VALUE CTOR" << endl + << "==========" << endl; + + // Testing value constructor. + { + const Uint64 BIG_VAL = 0xFFFFFFFFULL * 2; + const Uint64 MAX_RT = ULLONG_MAX/1000000; + + struct { + int d_line; + Uint64 d_sustRate; + Ti d_sustWindow; + Uint64 d_peakRate; + Ti d_peakWindow; + Ti d_creationTime; + Ti d_expSustWnd; + Ti d_expPeakWnd; + } DATA[] = { + + // LINE S_RATE S_WND P_RATE P_WND TCREATE EXP_S_WND EXP_P_WND + // ---- --------- --------- -------- ---------- -------- --------- --------- + {L_, 100, Ti( 1), 1000, Ti(0.001), Ti( 0), Ti( 1), Ti(0.001)}, + {L_, 10, Ti( 0.5), 150, Ti( 0.1), Ti( 0), Ti( 0.5), Ti( 0.1)}, + {L_, BIG_VAL, Ti(1000), MAX_RT, Ti( 50), Ti(999), Ti(1000), Ti( 50)}, + {L_, MAX_RT/2, Ti( 100), MAX_RT, Ti( 90), Ti(500), Ti( 100), Ti( 90)}, + {L_, 5, Ti( 0.3), 1000, Ti( 0.1), Ti( 0), Ti( 0.2), Ti( 0.1)}, + {L_, 1, Ti( 10), 10, Ti( 1.55), Ti( 0), Ti( 10), Ti( 1.5)}, + {L_, 1, Ti( 0.1), 100, Ti( 0.05), Ti( 0), Ti( 1), Ti( 0.05)} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + for (int ti = 0; ti < NUM_DATA; ++ti) { + const Uint64 LINE = DATA[ti].d_line; + const Uint64 S_RATE = DATA[ti].d_sustRate; + const Ti S_WND = DATA[ti].d_sustWindow; + const Uint64 P_RATE = DATA[ti].d_peakRate; + const Ti P_WND = DATA[ti].d_peakWindow; + const Ti CREATION_TIME = DATA[ti].d_creationTime; + const Ti EXP_S_WND = DATA[ti].d_expSustWnd; + const Ti EXP_P_WND = DATA[ti].d_expPeakWnd; + + Obj x(S_RATE, S_WND, P_RATE, P_WND, CREATION_TIME); + const Obj& X = x; + + LOOP_ASSERT(LINE, 0 == X.unitsReserved()); + LOOP_ASSERT(LINE, S_RATE == X.sustainedRateLimit()); + LOOP_ASSERT(LINE, P_RATE == X.peakRateLimit()); + LOOP_ASSERT(LINE, CREATION_TIME == X.lastUpdateTime()); + + LOOP_ASSERT(LINE, + CREATION_TIME == + X.statisticsCollectionStartTime()); + + Ti delta = EXP_S_WND - X.sustainedRateWindow(); + LOOP_ASSERT(LINE, + 0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + delta = EXP_P_WND - X.peakRateWindow(); + LOOP_ASSERT(LINE, + 0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + } + + // Testing Coexistence + { + Obj y(DATA[0].d_sustRate, + DATA[0].d_sustWindow, + DATA[0].d_peakRate, + DATA[0].d_peakWindow, + DATA[0].d_creationTime); + + Obj z(DATA[1].d_sustRate, + DATA[1].d_sustWindow, + DATA[1].d_peakRate, + DATA[1].d_peakWindow, + DATA[1].d_creationTime); + + ASSERT(DATA[0].d_sustRate == y.sustainedRateLimit()); + ASSERT(DATA[0].d_peakRate == y.peakRateLimit()); + ASSERT(DATA[0].d_creationTime == y.lastUpdateTime()); + ASSERT(DATA[0].d_creationTime == + y.statisticsCollectionStartTime()); + + Ti delta = DATA[0].d_sustWindow - y.sustainedRateWindow(); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + delta = DATA[0].d_peakWindow - y.peakRateWindow(); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + ASSERT(DATA[1].d_sustRate == z.sustainedRateLimit()); + ASSERT(DATA[1].d_peakRate == z.peakRateLimit()); + ASSERT(DATA[1].d_creationTime == z.lastUpdateTime()); + ASSERT(DATA[1].d_creationTime == + z.statisticsCollectionStartTime()); + + delta = DATA[1].d_sustWindow - z.sustainedRateWindow(); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + delta = DATA[1].d_peakWindow - z.peakRateWindow(); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + } + } + + // Negative Testing + { + bsls::AssertTestHandlerGuard hG; + + // Zero rate or zero window. + ASSERT_SAFE_FAIL(Obj x1(0, Ti(15), 10, Ti(10), Ti(0))); + ASSERT_SAFE_FAIL(Obj x1(1, Ti( 0), 10, Ti(10), Ti(0))); + ASSERT_SAFE_FAIL(Obj x1(1, Ti(15), 0, Ti(10), Ti(0))); + ASSERT_SAFE_FAIL(Obj x1(1, Ti(15), 10, Ti( 0), Ti(0))); + + // Negative window. + ASSERT_SAFE_FAIL(Obj x1(1, Ti(-15), 10, Ti(10), Ti(0))); + ASSERT_SAFE_FAIL(Obj x1(1, Ti(15), 10, Ti(-10), Ti(0))); + + } + } break; + case 1: { + // ---------------------------------------------------------------- + // BREATHING TEST: + // Developers' Sandbox. + // + // Plan: + // Perform and ad-hoc test of the primary modifiers and accessors. + // + // Testing: + // This "test" *exercises* basic functionality, but *tests* + // nothing. + // ---------------------------------------------------------------- + + if (verbose) cout << endl + << "BREATHING TEST" << endl + << "==============" << endl; + + Obj x(1, Ti(10), 1, Ti(10), Ti(0)); + ASSERT(1 == x.sustainedRateLimit()); + ASSERT(Ti(10) == x.sustainedRateWindow()); + ASSERT(1 == x.peakRateLimit()); + ASSERT(Ti(10) == x.peakRateWindow()); + ASSERT(Ti(0) == x.lastUpdateTime()); + ASSERT(Ti(0) == x.statisticsCollectionStartTime()); // C-10 + ASSERT(0 == x.unitsReserved()); + + Ti currentTime(1); + + x.setRateLimits(1000, Ti(1), 10000, Ti(0.1)); + x.reset(currentTime); + + ASSERT(1000 == x.sustainedRateLimit()); + ASSERT(Ti(1) == x.sustainedRateWindow()); + + x.submit(500); + x.reserve(250); + ASSERT(250 == x.unitsReserved()); + + ASSERT(false == x.wouldExceedBandwidth(currentTime)); + ASSERT(Ti(0) == x.calculateTimeToSubmit(currentTime)); + x.submitReserved(250); + x.submit(750); + + ASSERT(0 == x.unitsReserved()); + + Ti delta = Ti(0.501) - x.calculateTimeToSubmit(currentTime); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + currentTime.addMilliseconds(500); + ASSERT(0 == delta.seconds() && + delta.nanoseconds() >= 0 && + delta.nanoseconds() <= 1 ); + + currentTime.addMilliseconds(100); + x.updateState(currentTime); + + x.submit(100); + + } break; + default: { + cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; + testStatus = -1; + } break; + } + + if (testStatus > 0) { + cerr << "Error, non-zero test status = " << testStatus << "." << endl; + } + return testStatus; +} + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- \ No newline at end of file diff --git a/groups/bal/balb/balb_reservationguard.cpp b/groups/bal/balb/balb_reservationguard.cpp new file mode 100644 index 0000000000..8b596aa0a6 --- /dev/null +++ b/groups/bal/balb/balb_reservationguard.cpp @@ -0,0 +1,23 @@ +// balb_reservationguard.cpp -*-C++-*- +#include + +#include +BSLS_IDENT_RCSID(balb_reservationguard_cpp,"$Id$ $CSID$") + +#include // for testing only + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_reservationguard.h b/groups/bal/balb/balb_reservationguard.h new file mode 100644 index 0000000000..939e576908 --- /dev/null +++ b/groups/bal/balb/balb_reservationguard.h @@ -0,0 +1,274 @@ +// balb_reservationguard.h -*-C++-*- +#ifndef INCLUDED_BALB_RESERVATIONGUARD +#define INCLUDED_BALB_RESERVATIONGUARD + +#include +BSLS_IDENT("$Id: $") + +//@PURPOSE: Provide a generic proctor for rate controlling objects. +// +//@CLASSES: +// balb::ReservationGuard: a guard for reserving resources from rate limiters. +// +//@SEE_ALSO: balb_leakybucket, balb_ratelimiter +// +//@DESCRIPTION: This component provides generic proctor to automatically +// reserve and release units from a rate controlling object. The rate +// controlling object can be of any type (typically either a +// 'balb::RateLimiter' or 'balb::LeakyBucket') that provides the following +// methods: +//.. +// void reserve(bsls::Types::Uint64 numOfUnits); +// void submitReserved(bsls::Types::Uint64 numOfUnits); +// void cancelReserved(bsls::Types::Uint64 numOfUnits); +//.. +// Use 'balb::ReservationGuard' to ensure that reserved units will be correctly +// returned to a rate controlling object in a programming scope. Note that +// 'balb::ReservationGuard' does not assume ownership of the rate controlling +// object. +// +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Guarding units reservation in operations with balb::LeakyBucket +///-------------------------------------------------------------------------- +// Suppose that we are limiting the rate of network traffic generation using a +// 'balb::LeakyBucket' object. We send data buffer over a network interface +// using the 'mySendData' function: +//.. +// bsls::Types::Uint64 mySendData(size_t dataSize); +// // Send a specified 'dataSize' amount of data over the network. Return +// // the amount of data actually sent. Throw an exception if a network +// // failure is detected. +//.. +// Notice that the 'mySendData' function may throw an exception; therefore, we +// should wait until 'mySendData' returns before indicating the amount of data +// sent to the leaky bucket. +// +// Further suppose that multiple threads are sending network data and sharing +// the same leaky bucket. If every thread simply checks for overflowing of the +// leaky bucket, send data, and then submit to the leaky bucket, then the rate +// of data usage may exceed the limits imposed by the leaky bucket due to race +// conditions. We can avoid the this issue by reserving the amount of data +// immediately after checking whether the leaky bucket has overflown and submit +// the reserved amount after the data has been sent. However, this process +// could lead to the loss of the reserved units (effectively decreasing the +// leaky bucket's capacity) if 'mySendData' throws an exception. +// 'balb::ReservationGuard' is designed to resolve this issue. +// +// First, we define the size of each data chunk and the total size of the data +// to send: +//.. +// const bsls::Types::Uint64 CHUNK_SIZE = 256; +// bsls::Types::Uint64 bytesSent = 0; +// bsls::Types::Uint64 totalSize = 10 * 1024; // in bytes +//.. +// Then, we create a 'balb::LeakyBucket' object to limit the rate of data +// transmission: +//.. +// bsls::Types::Uint64 rate = 512; +// bsls::Types::Uint64 capacity = 1536; +// bsls::TimeInterval now = bdlt::CurrentTime::now(); +// balb::LeakyBucket bucket(rate, capacity, now); +//.. +// Next, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would cause the leaky bucket to overflow: +//.. +// while (bytesSent < totalSize) { +// +// now = bdlt::CurrentTime::now(); +// if (!bucket.wouldOverflow(now)) { +//.. +// Now, if the leaky bucket would not overflow, we create a +// 'balb::ReservationGuard' object to reserve the amount of data to be sent: +//.. +// balb::ReservationGuard guard(&bucket, +// CHUNK_SIZE); +//.. +// Then, we use the 'mySendData' function to send the data chunk over the +// network. After the data had been sent, we submit the amount of reserved +// data that was actually sent: +//.. +// bsls::Types::Uint64 result; +// result = mySendData(CHUNK_SIZE); +// bytesSent += result; +// guard.submitReserved(result); +//.. +// Note that we do not have manually cancel any remaining units reserved by the +// 'balb::ReservationGuard' object either because 'mySendData' threw an +// exception, or the data was only partially sent, because when the guard +// object goes out of scope, all remaining reserved units will be automatically +// cancelled. +//.. +// } +//.. +// Finally, if submitting another byte will cause the leaky bucket to overflow, +// then we wait until the submission will be allowed by waiting for an amount +// time returned by the 'calculateTimeToSubmit' method: +//.. +// else { +// bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit( +// now); +// bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + +// (timeToSubmit.nanoseconds() % 1000) ? 1 : 0; +// bslmt::ThreadUtil::microSleep(static_cast(uS)); +// } +// } +//.. + +#include + +#include +#include +#include + +namespace BloombergLP { +namespace balb { + + //======================= + // class ReservationGuard + //======================= + +template +class ReservationGuard { + // This class template implements a proctor for reserving and cancelling + // units in a rate controlling object. + // + // This class: + //: o is *exception* *neutral* (agnostic) + //: o is *const* *thread-safe* + // For terminology see 'bsldoc_glossary'. + + // DATA + TYPE *d_rateController_p; // Pointer to the rate + // controlling object in which + // the units are reserved. + + bsls::Types::Uint64 d_unitsReserved; // Number of units reserved by + // this object. + + private: + // NOT IMPLEMENTED + ReservationGuard(); + ReservationGuard& operator =(const ReservationGuard&); + ReservationGuard(const ReservationGuard&); + + public: + // CREATORS + ReservationGuard(TYPE* rateController, bsls::Types::Uint64 numUnits); + // Create a 'ReservationGuard' object guarding the specified + // 'rateController' and reserving the specified 'numUnits'. + + ~ReservationGuard(); + // Destroy this object. Invoke the 'cancelReserved' method for the + // remaining remaining units reserved by this proctor. + + // MANIPULATORS + void cancelReserved(bsls::Types::Uint64 numUnits); + // Cancel the specified 'numUnits' from the reserve units guarded by + // this object. Subtract the 'numUnits' from 'unitsReserved' and + // invoke the 'cancelReserved' method on the guarded object for + // 'numUnits'. After this operation, the number of reserved units + // guarded by this object will be reduced by 'numUnits'. The behavior + // is undefined unless 'numUnits <= unitsReserved()'. + + void submitReserved(bsls::Types::Uint64 numUnits); + // Submit the specified 'numUnits' from the reserve units guarded by + // this object. After this operation, the number of reserved units + // guarded by this object will be reduced by 'numUnits'. The behavior + // is undefined unless 'numUnits <= unitsReserved()'. + + // ACCESSORS + TYPE *ptr() const; + // Return a pointer to the rate controlling object used by this object. + + bsls::Types::Uint64 unitsReserved() const; + // Return the number of units reserved by this object. +}; + +// ============================================================================ +// INLINE FUNCTION DEFINITIONS +// ============================================================================ + + //----------------------- + // class ReservationGuard + //----------------------- + +// CREATORS +template +inline +ReservationGuard::ReservationGuard(TYPE* rateController, + bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(0 != rateController); + + d_rateController_p = rateController; + d_unitsReserved = numUnits; + + d_rateController_p->reserve(numUnits); +} + +template +inline +ReservationGuard::~ReservationGuard() +{ + d_rateController_p->cancelReserved(d_unitsReserved); +} + +// ACCESSORS +template +inline +TYPE *ReservationGuard::ptr() const +{ + return d_rateController_p; +} + +template +inline +bsls::Types::Uint64 ReservationGuard::unitsReserved() const +{ + return d_unitsReserved; +} + +// MANIPULATORS +template +inline +void ReservationGuard::cancelReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); + + d_rateController_p->cancelReserved(numUnits); + d_unitsReserved -= numUnits; +} + +template +inline +void ReservationGuard::submitReserved(bsls::Types::Uint64 numUnits) +{ + BSLS_ASSERT_SAFE(numUnits <= d_unitsReserved); + + d_rateController_p->submitReserved(numUnits); + d_unitsReserved -= numUnits; +} + +} // close package namespace +} // close enterprise namespace + +#endif + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/balb_reservationguard.t.cpp b/groups/bal/balb/balb_reservationguard.t.cpp new file mode 100644 index 0000000000..03925a3728 --- /dev/null +++ b/groups/bal/balb/balb_reservationguard.t.cpp @@ -0,0 +1,787 @@ +// balb_reservationguard.t.cpp -*-C++-*- +#include + +#include // for testing only + +#include + +#include + +#include + +#include + +using namespace BloombergLP; +using namespace bsl; + +//============================================================================= +// TEST PLAN +//----------------------------------------------------------------------------- +// Overview +// -------- +// The component under test implements a mechanism. +// +// Primary Manipulators: +// o 'submitReserved' +// o 'cancelReserved' +// +// Basic Accessors: +// o 'ptr' +// o 'unitsReserved' +// +// This class also provides a value constructor capable of creating an object +// having any parameters. +// +// Global Concerns: +//: o ACCESSOR methods are declared 'const'. +//: o CREATOR & MANIPULATOR pointer/reference parameters are declared 'const'. +//: o Precondition violations are detected in appropriate build modes. +// +// Global Assumptions: +//: o ACCESSOR methods are 'const' thread-safe. +//----------------------------------------------------------------------------- +// +// CREATORS +// [4] ~balb::ReservationGuard(); +// [3] balb::ReservationGuard(TYPE* reserve, bsls::Types::Uint64 numOfUnits); +// +// MANIPULATORS +// [6] void cancelReserved(bsls::Types::Uint64 numOfUnits); +// [5] void submitReserved(bsls::Types::Uint64 numOfUnits); +// +// ACCESSORS +// [3] bsls::Types::Uint64 unitsReserved() const; +// [3] TYPE *ptr() const; +//----------------------------------------------------------------------------- +// [1] BREATHING TEST +// [2] TEST APPARATUS +// [7] USAGE EXAMPLE +// [3] All accessor methods are declared 'const'. +// [*] All creator/manipulator ptr./ref. parameters are 'const'. +//============================================================================= + +//============================================================================= +// STANDARD BDE ASSERT TEST MACRO +//----------------------------------------------------------------------------- +static int testStatus = 0; + +static void aSsErT(int c, const char *s, int i) +{ + if (c) { + cout << "Error " << __FILE__ << "(" << i << "): " << s + << " (failed)" << endl; + if (testStatus >= 0 && testStatus <= 100) ++testStatus; + } +} + +# define ASSERT(X) { aSsErT(!(X), #X, __LINE__); } +//----------------------------------------------------------------------------- +#define LOOP_ASSERT(I,X) { \ + if (!(X)) { cout << #I << ": " << I << "\n"; aSsErT(1, #X, __LINE__); }} + +#define LOOP2_ASSERT(I,J,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " \ + << J << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP3_ASSERT(I,J,K,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" \ + << #K << ": " << K << "\n"; aSsErT(1, #X, __LINE__); } } + +#define LOOP4_ASSERT(I,J,K,L,X) { \ + if (!(X)) { cout << #I << ": " << I << "\t" << #J << ": " << J << "\t" << \ + #K << ": " << K << "\t" << #L << ": " << L << "\n"; \ + aSsErT(1, #X, __LINE__); } } + +#define LOOP0_ASSERT ASSERT +#define LOOP1_ASSERT LOOP_ASSERT + +//============================================================================= +// STANDARD BDE VARIADIC ASSERT TEST MACROS +//----------------------------------------------------------------------------- + +#define NUM_ARGS_IMPL(X5, X4, X3, X2, X1, X0, N, ...) N +#define NUM_ARGS(...) NUM_ARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1, 0, "") + +#define LOOPN_ASSERT_IMPL(N, ...) LOOP ## N ## _ASSERT(__VA_ARGS__) +#define LOOPN_ASSERT(N, ...) LOOPN_ASSERT_IMPL(N, __VA_ARGS__) + +#define ASSERTV(...) LOOPN_ASSERT(NUM_ARGS(__VA_ARGS__), __VA_ARGS__) + +// ============================================================================ +// SEMI-STANDARD TEST OUTPUT MACROS +// ---------------------------------------------------------------------------- + +#define P(X) cout << #X " = " << (X) << endl; // Print identifier and value. +#define Q(X) cout << "<| " #X " |>" << endl; // Quote identifier literally. +#define P_(X) cout << #X " = " << (X) << ", " << flush; // 'P(X)' without '\n' +#define T_ cout << "\t" << flush; // Print tab w/o newline. +#define L_ __LINE__ // current Line number + +// ============================================================================ +// NEGATIVE-TEST MACRO ABBREVIATIONS +// ---------------------------------------------------------------------------- + +#define ASSERT_PASS(EXPR) BSLS_ASSERTTEST_ASSERT_PASS(EXPR) +#define ASSERT_SAFE_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_SAFE_FAIL(EXPR) +#define ASSERT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_FAIL(EXPR) +#define ASSERT_OPT_FAIL(EXPR) BSLS_ASSERTTEST_ASSERT_OPT_FAIL(EXPR) + +//============================================================================= +// GLOBAL TYPEDEFS/CONSTANTS FOR TESTING +//----------------------------------------------------------------------------- + +class my_Reserve { + // This class provides a mock rate controllering mechanism which conforms + // to the interface required by 'balb::ReservationGuard'. + + + // DATA + bsls::Types::Uint64 d_unitsSubmitted; + bsls::Types::Uint64 d_unitsReserved; + +public: + // CREATORS + my_Reserve(); + // Create a 'my_Reserve' object having zero 'unitsReserved' and + // 'unitsSubmitted' + + // MANIPULATORS + void reserve(bsls::Types::Uint64 numUnits); + // Add the specified 'numUnits' to the 'unitsReserved' counter. + + void submitReserved(bsls::Types::Uint64 numUnits); + // Subtract the specified 'numUnits' from 'unitsReserved' and add it + // to 'unitsSubmitted', if 'numUnits' is less than 'unitsReserved'. + // Otherwise, add 'unitsReserved' to 'unitsSubmitted' and set + // 'unitsReserved' to 0. + + void cancelReserved(bsls::Types::Uint64 numUnits); + // Subtract the specified 'numUnits' from 'unitsReserved' if 'numUnits' + // is less than 'unitsReserved'; otherwise, set 'unitsReserved' to 0. + + void reset(); + // Reset this 'my_Reserve' object to its default-constructed state. + + // ACCESSORS + bsls::Types::Uint64 unitsReserved() const; + // Return the number of units reserved. + + bsls::Types::Uint64 unitsSubmitted() const; + // Return the number of units submitted. +}; + +// CREATORS +inline +my_Reserve::my_Reserve() +: d_unitsSubmitted(0) +, d_unitsReserved(0) +{ +} + +// MANIPULATORS +inline +void my_Reserve::reserve(bsls::Types::Uint64 numOfUnits) +{ + d_unitsReserved += numOfUnits; +} + +inline +void my_Reserve::submitReserved(bsls::Types::Uint64 numOfUnits) +{ + if (numOfUnits < d_unitsReserved) { + d_unitsReserved -= numOfUnits; + } + else { + d_unitsReserved = 0; + } + d_unitsSubmitted += numOfUnits; +} + +inline +void my_Reserve::cancelReserved(bsls::Types::Uint64 numOfUnits) +{ + if (numOfUnits < d_unitsReserved) { + d_unitsReserved -= numOfUnits; + } + else { + d_unitsReserved = 0; + } +} + +inline +void my_Reserve::reset() +{ + d_unitsReserved = 0; + d_unitsSubmitted = 0; +} + +// ACCESSORS +inline +bsls::Types::Uint64 my_Reserve::unitsReserved() const +{ + return d_unitsReserved; +} + +inline +bsls::Types::Uint64 my_Reserve::unitsSubmitted() const +{ + return d_unitsSubmitted; +} + + +typedef balb::ReservationGuard Obj; +typedef bsls::TimeInterval Ti; +typedef bsls::Types::Uint64 Uint64; +typedef unsigned int uint; + +//============================================================================= +// USAGE EXAMPLE +//----------------------------------------------------------------------------- + +///Usage +///----- +// This section illustrates the intended use of this component. +// +///Example 1: Guarding units reservation in operations with balb::LeakyBucket +///-------------------------------------------------------------------------- +// Suppose that we are limiting the rate of network traffic generation using a +// 'balb::LeakyBucket' object. We send data buffer over a network interface +// using the 'mySendData' function: +//.. +static bsls::Types::Uint64 sendData(size_t dataSize) + // Send a specified 'dataSize' amount of data over the network. Return + // the amount of data actually sent. Throw an exception if a network + // failure is detected. + +{ +//.. +// In our example we don`t deal with actual data sending, so we assume that the +// function has sent certain amount of data (3/4 of the requested amount) +// successfully. +//.. + return (dataSize*3)>>2; +} +//.. +// Notice that the 'mySendData' function may throw an exception; therefore, we +// should wait until 'mySendData' returns before indicating the amount of data +// sent to the leaky bucket. + + +//============================================================================= +// MAIN PROGRAM +//----------------------------------------------------------------------------- + +int main(int argc, char *argv[]) { + + int test = argc > 1 ? atoi(argv[1]) : 0; + bool verbose = argc > 2; + // bool veryVerbose = argc > 3; + // bool veryVeryVerbose = argc > 4; + + cout << "TEST " << __FILE__ << " CASE " << test << endl; + + switch (test) { case 0: + case 7: { + // -------------------------------------------------------------------- + // USAGE EXAMPLE + // The usage example provided in the component header file must + // compile, link, and run on all platforms as shown. + // + // Testing: + // USAGE EXAMPLE + //--------------------------------------------------------------------- + + if (verbose) cout << endl + << "USAGE EXAMPLE" << endl + << "=============" << endl; + +// Further suppose that multiple threads are sending network data and sharing +// the same leaky bucket. If every thread simply checks for overflowing of the +// leaky bucket, send data, and then submit to the leaky bucket, then the rate +// of data usage may exceed the limitations imposed by the leaky bucket due to +// race conditions. We can avoid the this issue by reserving the amount of +// data immediately after checking whether the leaky bucket has overflown and +// submit the reserved amount after the data has been sent. However, this +// procedure could lead to a permanent loss of the leaky bucket capacity if +// 'mySendData' throws an exception and the reserved units are not cancelled +// -- the issue that 'balb::ReservationGuard' is designed to solve. +// +// First, we define the size of each data chunk and the total size of the data +// to send: +//.. + const bsls::Types::Uint64 CHUNK_SIZE = 256; + bsls::Types::Uint64 bytesSent = 0; + bsls::Types::Uint64 totalSize = 10 * 1024; // in bytes +//.. +// Then, we create a 'balb::LeakyBucket' object to limit the rate of data +// transmission: +//.. + bsls::Types::Uint64 rate = 512; + bsls::Types::Uint64 capacity = 1536; + bsls::TimeInterval now = bdlt::CurrentTime::now(); + balb::LeakyBucket bucket(rate, capacity, now); +//.. +// Next, we send the chunks of data using a loop. For each iteration, we check +// whether submitting another byte would cause the leaky bucket to overflow: +//.. + while (bytesSent < totalSize) { + + now = bdlt::CurrentTime::now(); + if (!bucket.wouldOverflow(now)) { +//.. +// Now, if the leaky bucket would not overflow, we create a +// 'balb::ReservationGuard' object to reserve the amount of data to be sent: +//.. + balb::ReservationGuard guard(&bucket, + CHUNK_SIZE); +//.. +// Then, we use the 'mySendData' function to send the data chunk over the +// network. After the data had been sent, we submit the amount of reserved +// data that was actually sent: +//.. + bsls::Types::Uint64 result; + result = sendData(CHUNK_SIZE); + bytesSent += result; + guard.submitReserved(result); +//.. +// Note that we do not have manually cancel any remaining units reserved by the +// 'balb::ReservationGuard' object either because 'mySendData' threw an +// exception, or the data was only partially sent, because when the guard +// object goes out of scope, all remaining reserved units will be automatically +// cancelled. +//.. + } +//.. +// Finally, if submitting another byte will cause the leaky bucket to overflow, +// then we wait until the submission will be allowed by waiting for an amount +// time returned by the 'calculateTimeToSubmit' method: +//.. + else { + bsls::TimeInterval timeToSubmit = bucket.calculateTimeToSubmit(now); + bsls::Types::Uint64 uS = timeToSubmit.totalMicroseconds() + + (timeToSubmit.nanoseconds() % 1000) ? 1 : 0; + bslmt::ThreadUtil::microSleep(static_cast(uS)); + } + } +//.. + } break; + case 6: { + // -------------------------------------------------------------------- + // CLASS METHOD 'cancelReserved' + // + // Concerns: + //: 1 The method cancels the specified number of units in the + //: object specified at construction. + //: + //: 2 The method decrements 'unitsReserved' counter. + //: + //: 3 The method submits no units. + // + // Testing: + // void cancelReserved(bsls::Types::Uint64 numOfUnits); + //--------------------------------------------------------------------- + + if (verbose) cout << endl + << "CLASS METHOD 'cancelReserved'" << endl + << "=============================" << endl; + + const Uint64 INIT_RESERVE = 42; + + struct { + + int d_line; + Uint64 d_unitsToReserve; + Uint64 d_unitsToCancel; + Uint64 d_numOfCancels; + + } DATA[] = { + + // LINE RESERVE CANCEL NUM_OF_CANCELS + // ---- -------- ------------- -------------- + {L_, 42, 10, 1}, + {L_, ULLONG_MAX / 2, ULLONG_MAX / 4, 2}, + {L_, 50, 1, 49} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for (int ti = 0; ti < NUM_DATA; ++ti) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 RESERVE = DATA[ti].d_unitsToReserve; + const Uint64 CANCEL = DATA[ti].d_unitsToCancel; + const Uint64 N_CANCELS = DATA[ti].d_numOfCancels; + + my_Reserve h; + h.reserve(INIT_RESERVE); + + { + Obj x(&h, RESERVE); + + for (unsigned int i = 1; i <= N_CANCELS; i++) { + + bsls::Types::Uint64 expGuardReserve = + (i*CANCEL > RESERVE ? 0 : (RESERVE - i*CANCEL)); + + bsls::Types::Uint64 expReserve = INIT_RESERVE + + expGuardReserve; + + x.cancelReserved(CANCEL); + + // C-1 + + LOOP_ASSERT(LINE, expReserve == h.unitsReserved()); + + // C-3 + + LOOP_ASSERT(LINE, 0 == h.unitsSubmitted()); + + // C-2 + + LOOP_ASSERT(LINE, expGuardReserve == + x.unitsReserved()); + } + } + + LOOP_ASSERT(LINE, INIT_RESERVE == h.unitsReserved()); + + } + + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + my_Reserve r; + Obj y(&r, 42); + + ASSERT_SAFE_FAIL(y.cancelReserved(43)); + ASSERT_PASS(y.cancelReserved(42)); + } + + } break; + case 5: { + // -------------------------------------------------------------------- + // CLASS METHOD 'submitReserved' + // + // Concerns: + //: 1 The method submits the specified number of units into the + //: object specified at construction. + //: + //: 2 The method decrements the 'unitsReserved' counter. + // + // Testing: + // void submitReserved(bsls::Types::Uint64 numOfUnits); + //--------------------------------------------------------------------- + + if (verbose) cout << endl + << "CLASS METHOD 'submitReserved'" << endl + << "=============================" << endl; + + struct { + int d_line; + Uint64 d_unitsToReserve; + Uint64 d_unitsToSubmit; + Uint64 d_numOfSubmits; + } DATA[] = { + // LINE RESERVE SUBMIT NUM_OF_SUBMITS + // ---- -------- ------------- -------------- + {L_, 42, 10, 1}, + {L_, 1000, 750, 1}, + {L_, ULLONG_MAX / 2, ULLONG_MAX / 4, 2} + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + for (int ti = 0; ti < NUM_DATA; ++ti) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 RESERVE = DATA[ti].d_unitsToReserve; + const Uint64 SUBMIT = DATA[ti].d_unitsToSubmit; + const Uint64 N_SUBMITS = DATA[ti].d_numOfSubmits; + + my_Reserve h; + + { + Obj x(&h, RESERVE); + + for (unsigned int i = 1; i <= N_SUBMITS; i++) { + + bsls::Types::Uint64 expReserve = + i*SUBMIT > RESERVE ? 0 : (RESERVE - i*SUBMIT); + + x.submitReserved(SUBMIT); + + // C-1 + + LOOP_ASSERT(LINE, i*SUBMIT == h.unitsSubmitted()); + LOOP_ASSERT(LINE, expReserve == h.unitsReserved()); + + // C-2 + + LOOP_ASSERT(LINE, expReserve == x.unitsReserved()); + } + } + + } + + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + my_Reserve r; + Obj y(&r, 42); + + ASSERT_SAFE_FAIL(y.submitReserved(43)); + ASSERT_PASS(y.submitReserved(42)); + } + } break; + case 4: { + // -------------------------------------------------------------------- + // DTOR + // + // Concerns: + //: 1 DTOR cancels all 'unitsReserved' in the 'reserve' object, + //: specified at the construction. + //: + //: 2 DTOR submits no units. + //: + //: 3 DTOR operates correctly if no units were reserved at + //: creation. + // + // Testing: + // ~ReservationGuard(); + //--------------------------------------------------------------------- + + if (verbose) cout << endl + << "DTOR" << endl + << "====" << endl; + + const bsls::Types::Uint64 INIT_SUBMITTED = 5; + const bsls::Types::Uint64 INIT_RESERVED = 37; + + struct { + + int d_line; + Uint64 d_unitsToReserve; + + } DATA[] = { + + // LINE RESERVE + // ---- -------- + {L_, 42, }, + {L_, 1000, }, + {L_, 0, }, // C-3 + {L_, ULLONG_MAX/2 }, + }; + const int NUM_DATA = sizeof(DATA)/sizeof(*DATA); + + my_Reserve h; + h.reserve(INIT_SUBMITTED+INIT_RESERVED); + h.submitReserved(INIT_SUBMITTED); + + for (int ti = 0; ti < NUM_DATA; ++ti) { + + const Uint64 LINE = DATA[ti].d_line; + const Uint64 RESERVE = DATA[ti].d_unitsToReserve; + + { + Obj x(&h, RESERVE); + LOOP_ASSERT(LINE, + INIT_RESERVED + RESERVE == h.unitsReserved()); + } + + LOOP_ASSERT(LINE, INIT_SUBMITTED == h.unitsSubmitted()); // C-1 + LOOP_ASSERT(LINE, INIT_RESERVED == h.unitsReserved()); // C-2 + } + + } break; + case 3: { + // -------------------------------------------------------------------- + // PRIMARY MANIPULATOR (BOOTSTRAP) + // + // Concerns: + //: 1 Units in the specified 'reserve' are reserved during the + //: construction + //: + //: 2 'ptr' returns pointer to the 'reserve' object, specified during + //: the construction + //: + //: 3 'unitsReserved' return the number of units reserved in the + //: specified 'reserve' object + //: + //: 4 'unitsReserved' is represented with 64-bit integral type + //: + //: 5 Specifying wrong parameters for constructor causes certain + //: behavior in specific build configuration. + // + // Testing: + // ReservationGuard(TYPE* reserve, bsls::Types::Uint64 numOfUnits); + // TYPE* ptr() const; + // bsls::Types::Uint64 unitsReserved() const; + //--------------------------------------------------------------------- + + if (verbose) cout << endl + << "PRIMARY MANIPULATOR (BOOTSTRAP)" << endl + << "===============================" << endl; + + my_Reserve h; + + Obj x(&h, 1000); + + ASSERT(1000 == h.unitsReserved()); // C-1 + ASSERT(1000 == x.unitsReserved()); // C-3 + + ASSERT(&h == x.ptr()); // C-2 + + h.reset(); + + // C-4 + + Obj y(&h, ULLONG_MAX); + ASSERT(ULLONG_MAX == h.unitsReserved()); + ASSERT(ULLONG_MAX == y.unitsReserved()); + + // C-5 + + if (verbose) cout << endl << "Negative Testing" << endl; + { + bsls::AssertTestHandlerGuard hG; + + ASSERT_SAFE_FAIL(Obj x(0,42)); + } + + } break; + case 2: { + // -------------------------------------------------------------------- + // TEST APPARATUS + // + // Concerns: + //: 1 'Reserve' method adds units to 'unitsReserved'. + //: + //: 2 'Reserve' method does not affect 'unitsSubmitted'. + //: + //: 3 'submitReserved' method adds units to 'unitsSubmitted' and + //: subtracts units from 'unitsReserved'. + //: + //: 4 'cancelReserved' method subtracts units from 'unitsReserved'. + //: + //: 5 'cancelReserved' method does not affect 'unitsSubmitted'. + //: + //: 6 'unitsSubmitted' is represented by 64-bit unsigned integral + //: type. + //: + //: 7 'unitsReserved' is represented by 64-bit unsigned integral + //: type. + //: + //: 8 'unitsReserved' and 'unitSubmitted' are wired to the + //: different places. + //: + //: 9 'reset' method resets the object to its default-constructed + //: state. + // + // Testing: + // class 'my_Reserve' + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "TEST APPARATUS" << endl + << "==============" << endl; + + my_Reserve x; + + ASSERT(0 == x.unitsReserved()); + ASSERT(0 == x.unitsSubmitted()); + + x.reserve(42); + ASSERT(42 == x.unitsReserved()); // C-1 + ASSERT(0 == x.unitsSubmitted()); // C-2 + + // C-3, C-9 + + x.submitReserved(20); + ASSERT(22 == x.unitsReserved()); + ASSERT(20 == x.unitsSubmitted()); + + x.cancelReserved(22); + ASSERT(0 == x.unitsReserved()); // C-4 + ASSERT(20 == x.unitsSubmitted()); // C-5 + + // C-6, C-7 + + my_Reserve y; + + y.reserve(ULLONG_MAX); + ASSERT(ULLONG_MAX == y.unitsReserved()); + + y.submitReserved(ULLONG_MAX/4); + ASSERT(ULLONG_MAX/4 == y.unitsSubmitted()); + ASSERT(ULLONG_MAX - ULLONG_MAX/4 == y.unitsReserved()); + + y.cancelReserved(ULLONG_MAX/4); + ASSERT(ULLONG_MAX - 2*(ULLONG_MAX/4) == y.unitsReserved()); + + // C-9 + + y.reset(); + ASSERT(0 == y.unitsReserved()); + ASSERT(0 == y.unitsSubmitted()); + + } break; + case 1: { + // -------------------------------------------------------------------- + // BREATHING TEST: + // Developers' Sandbox. + // + // Plan: + // Perform and ad-hoc test of the primary modifiers and accessors. + // + // Testing: + // This "test" *exercises* basic functionality, but *tests* + // nothing. + // -------------------------------------------------------------------- + + if (verbose) cout << endl + << "BREATHING TEST" << endl + << "==============" << endl; + + my_Reserve h; + + { + Obj x(&h,500); + + ASSERT(500 == h.unitsReserved()); + + x.submitReserved(200); + ASSERT(200 == h.unitsSubmitted()); + ASSERT(300 == h.unitsReserved()); + } + + ASSERT(0 == h.unitsReserved()); + ASSERT(200 == h.unitsSubmitted()); + + } break; + default: { + cerr << "WARNING: CASE `" << test << "' NOT FOUND." << endl; + testStatus = -1; + } break; + } + + if (testStatus > 0) { + cerr << "Error, non-zero test status = " << testStatus << "." << endl; + } + return testStatus; +} + +// ---------------------------------------------------------------------------- +// Copyright 2021 Bloomberg Finance L.P. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ----------------------------- END-OF-FILE ---------------------------------- diff --git a/groups/bal/balb/doc/balb.txt b/groups/bal/balb/doc/balb.txt index 895170a79b..1d543eb1a0 100644 --- a/groups/bal/balb/doc/balb.txt +++ b/groups/bal/balb/doc/balb.txt @@ -20,18 +20,31 @@ : o A multi-process performance reporting mechanism and a value-semantic type : to represent metrics. Note that the entire {'balm'} package is dedicated : to metrics gathering. +: +: o A mechanism that allows clients to monitor whether a resource is being +: consumed at a particular rate. +: +: o A mechanism that enables clients to monitor and control the use of a +: resource such that the peak consumption rate and the sustained consumption +: rate do not exceed their respective configured limits. +: +: o Generic proctor to automatically reserve and release units from a rate +: controlling object. /Hierarchical Synopsis /--------------------- - The 'balb' package currently has 6 components having 2 levels of physical + The 'balb' package currently has 9 components having 2 levels of physical dependency. The list below shows the hierarchical ordering of the components. The order of components within each level is not architecturally significant, just alphabetical. .. 2. balb_filecleanerutil + balb_ratelimiter + balb_reservationguard 1. balb_controlmanager balb_filecleanerconfiguration + balb_leakybucket balb_performancemonitor balb_pipecontrolchannel balb_testmessages @@ -48,11 +61,20 @@ : 'balb_filecleanerutil': : Provide a utility class for configuration-based file removal. : +: 'balb_leakybucket': +: Provide a mechanism to monitor the consumption rate of a resource. +: : 'balb_performancemonitor': : Provide a mechanism to collect process performance measures. : : 'balb_pipecontrolchannel': : Provide a mechanism for reading control messages from a named pipe. : +: 'balb_ratelimiter': +: Provide a mechanism to limit peak and sustained consumption rates. +: +: 'balb_reservationguard': +: Provide a generic proctor for rate controlling objects. +: : 'balb_testmessages': : Provide value-semantic attribute classes. diff --git a/groups/bal/balb/package/balb.mem b/groups/bal/balb/package/balb.mem index 2a58e97210..e0035acc9a 100644 --- a/groups/bal/balb/package/balb.mem +++ b/groups/bal/balb/package/balb.mem @@ -1,6 +1,9 @@ balb_controlmanager balb_filecleanerconfiguration balb_filecleanerutil +balb_leakybucket balb_performancemonitor balb_pipecontrolchannel +balb_ratelimiter +balb_reservationguard balb_testmessages diff --git a/groups/bal/balber/balber.xsd b/groups/bal/balber/balber.xsd index e937c2fad0..30958f25eb 100644 --- a/groups/bal/balber/balber.xsd +++ b/groups/bal/balber/balber.xsd @@ -1,15 +1,15 @@ - - + + Schema of options records for balber codec @@ -147,6 +147,31 @@ + + + + This *backward*-*compatibility* option controls whether or not + negative zero floating-point values are encoded as the BER + representation of negative zero or positive zero. If this option + is 'true', negative zero floating-point values are encoded as the + BER representation of negative zero, such that they will decode + back to negative zero. If this option is 'false', negative zero + floating-point values are encoded as the BER representation of + positive zero, such they they will decode to positive zero. For + backward-compatibility purposes, the default value of this option + is 'false'. Setting this option to 'true' requires the receiving + decoder to come from BDE release '3.90.x' or later, or otherwise + comply to the ISO/IEC 8825-1:2015 standard. Clients are + encouraged to update their recipients to the latest version of BDE + and set this option to 'true'. Note that the 'false' value of + this option will eventually be deprecated, and the default value + changed to 'true'. + + + diff --git a/groups/bal/balber/balber_berencoder.t.cpp b/groups/bal/balber/balber_berencoder.t.cpp index 1c6892c3e1..9179e59393 100644 --- a/groups/bal/balber/balber_berencoder.t.cpp +++ b/groups/bal/balber/balber_berencoder.t.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include diff --git a/groups/bal/balber/balber_berencoderoptions.cpp b/groups/bal/balber/balber_berencoderoptions.cpp index f3aadc48ef..82df95a225 100644 --- a/groups/bal/balber/balber_berencoderoptions.cpp +++ b/groups/bal/balber/balber_berencoderoptions.cpp @@ -1,309 +1,137 @@ -// balber_berencoderoptions.cpp - GENERATED FILE - -*-C++-*- - -// ---------------------------------------------------------------------------- -// NOTICE -// -// This component is not up to date with current BDE coding standards, and -// should not be used as an example for new development. -// ---------------------------------------------------------------------------- - -#include +// balber_berencoderoptions.cpp *DO NOT EDIT* @generated -*-C++-*- #include BSLS_IDENT_RCSID(balber_berencoderoptions_cpp,"$Id$ $CSID$") -#include +#include + #include #include #include #include #include +#include #include -#include -#include -#include +#include +#include +#include namespace BloombergLP { +namespace balber { - // ------------------------------- - // class balber::BerEncoderOptions - // ------------------------------- + // ----------------------- + // class BerEncoderOptions + // ----------------------- // CONSTANTS -const char balber::BerEncoderOptions::CLASS_NAME[] = - "balber::BerEncoderOptions"; -const int balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_TRACE_LEVEL = 0; -const int balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE = 10500; -const bool balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS = true; -const bool balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = false; -const int balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION = 3; -const bool balber::BerEncoderOptions:: - DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING = false; - -const bdlat_AttributeInfo balber::BerEncoderOptions::ATTRIBUTE_INFO_ARRAY[] = { + +const char BerEncoderOptions::CLASS_NAME[] = "BerEncoderOptions"; + +const int BerEncoderOptions::DEFAULT_INITIALIZER_TRACE_LEVEL = 0; + +const int BerEncoderOptions::DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE = 10500; + +const bool BerEncoderOptions::DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS = true; + +const bool BerEncoderOptions::DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = false; + +const int BerEncoderOptions::DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION = 3; + +const bool BerEncoderOptions::DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING = false; + +const bool BerEncoderOptions::DEFAULT_INITIALIZER_PRESERVE_SIGN_OF_NEGATIVE_ZERO = false; + +const bdlat_AttributeInfo BerEncoderOptions::ATTRIBUTE_INFO_ARRAY[] = { { - e_ATTRIBUTE_ID_TRACE_LEVEL, + ATTRIBUTE_ID_TRACE_LEVEL, "TraceLevel", sizeof("TraceLevel") - 1, "", bdlat_FormattingMode::e_DEC }, { - e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE, + ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE, "BdeVersionConformance", sizeof("BdeVersionConformance") - 1, "", bdlat_FormattingMode::e_DEC }, { - e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS, + ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS, "EncodeEmptyArrays", sizeof("EncodeEmptyArrays") - 1, "", bdlat_FormattingMode::e_TEXT }, { - e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY, + ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY, "EncodeDateAndTimeTypesAsBinary", sizeof("EncodeDateAndTimeTypesAsBinary") - 1, "", bdlat_FormattingMode::e_TEXT }, { - e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION, + ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION, "DatetimeFractionalSecondPrecision", sizeof("DatetimeFractionalSecondPrecision") - 1, "", bdlat_FormattingMode::e_DEC }, { - e_ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING, + ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING, "DisableUnselectedChoiceEncoding", sizeof("DisableUnselectedChoiceEncoding") - 1, "", bdlat_FormattingMode::e_TEXT + }, + { + ATTRIBUTE_ID_PRESERVE_SIGN_OF_NEGATIVE_ZERO, + "PreserveSignOfNegativeZero", + sizeof("PreserveSignOfNegativeZero") - 1, + "", + bdlat_FormattingMode::e_TEXT } }; -namespace balber { - // CLASS METHODS + const bdlat_AttributeInfo *BerEncoderOptions::lookupAttributeInfo( - const char *name, - int nameLength) + const char *name, + int nameLength) { - switch(nameLength) { - case 10: { - if (name[0]=='T' - && name[1]=='r' - && name[2]=='a' - && name[3]=='c' - && name[4]=='e' - && name[5]=='L' - && name[6]=='e' - && name[7]=='v' - && name[8]=='e' - && name[9]=='l') - { - return &ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]; - // RETURN - } - } break; - case 17: { - if (name[0]=='E' - && name[1]=='n' - && name[2]=='c' - && name[3]=='o' - && name[4]=='d' - && name[5]=='e' - && name[6]=='E' - && name[7]=='m' - && name[8]=='p' - && name[9]=='t' - && name[10]=='y' - && name[11]=='A' - && name[12]=='r' - && name[13]=='r' - && name[14]=='a' - && name[15]=='y' - && name[16]=='s') - { - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]; - // RETURN - } - } break; - case 21: { - if (name[0]=='B' - && name[1]=='d' - && name[2]=='e' - && name[3]=='V' - && name[4]=='e' - && name[5]=='r' - && name[6]=='s' - && name[7]=='i' - && name[8]=='o' - && name[9]=='n' - && name[10]=='C' - && name[11]=='o' - && name[12]=='n' - && name[13]=='f' - && name[14]=='o' - && name[15]=='r' - && name[16]=='m' - && name[17]=='a' - && name[18]=='n' - && name[19]=='c' - && name[20]=='e') - { - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]; - // RETURN - } - } break; - case 30: { - if (name[0]=='E' - && name[1]=='n' - && name[2]=='c' - && name[3]=='o' - && name[4]=='d' - && name[5]=='e' - && name[6]=='D' - && name[7]=='a' - && name[8]=='t' - && name[9]=='e' - && name[10]=='A' - && name[11]=='n' - && name[12]=='d' - && name[13]=='T' - && name[14]=='i' - && name[15]=='m' - && name[16]=='e' - && name[17]=='T' - && name[18]=='y' - && name[19]=='p' - && name[20]=='e' - && name[21]=='s' - && name[22]=='A' - && name[23]=='s' - && name[24]=='B' - && name[25]=='i' - && name[26]=='n' - && name[27]=='a' - && name[28]=='r' - && name[29]=='y') - { - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]; - // RETURN - } - } break; - case 31: { - if (name[0]=='D' - && name[1]=='i' - && name[2]=='s' - && name[3]=='a' - && name[4]=='b' - && name[5]=='l' - && name[6]=='e' - && name[7]=='U' - && name[8]=='n' - && name[9]=='s' - && name[10]=='e' - && name[11]=='l' - && name[12]=='e' - && name[13]=='c' - && name[14]=='t' - && name[15]=='e' - && name[16]=='d' - && name[17]=='C' - && name[18]=='h' - && name[19]=='o' - && name[20]=='i' - && name[21]=='c' - && name[22]=='e' - && name[23]=='E' - && name[24]=='n' - && name[25]=='c' - && name[26]=='o' - && name[27]=='d' - && name[28]=='i' - && name[29]=='n' - && name[30]=='g') - { - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]; - } - } break; - case 33: { - if (name[0]=='D' - && name[1]=='a' - && name[2]=='t' - && name[3]=='e' - && name[4]=='t' - && name[5]=='i' - && name[6]=='m' - && name[7]=='e' - && name[8]=='F' - && name[9]=='r' - && name[10]=='a' - && name[11]=='c' - && name[12]=='t' - && name[13]=='i' - && name[14]=='o' - && name[15]=='n' - && name[16]=='a' - && name[17]=='l' - && name[18]=='S' - && name[19]=='e' - && name[20]=='c' - && name[21]=='o' - && name[22]=='n' - && name[23]=='d' - && name[24]=='P' - && name[25]=='r' - && name[26]=='e' - && name[27]=='c' - && name[28]=='i' - && name[29]=='s' - && name[30]=='i' - && name[31]=='o' - && name[32]=='n') - { - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]; - } - } break; + for (int i = 0; i < 7; ++i) { + const bdlat_AttributeInfo& attributeInfo = + BerEncoderOptions::ATTRIBUTE_INFO_ARRAY[i]; + + if (nameLength == attributeInfo.d_nameLength + && 0 == bsl::memcmp(attributeInfo.d_name_p, name, nameLength)) + { + return &attributeInfo; + } } + return 0; } const bdlat_AttributeInfo *BerEncoderOptions::lookupAttributeInfo(int id) { switch (id) { - case e_ATTRIBUTE_ID_TRACE_LEVEL: - return &ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]; - case e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]; - case e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: - return &ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]; - case e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]; - case e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]; - case e_ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: - return &ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]; + case ATTRIBUTE_ID_TRACE_LEVEL: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TRACE_LEVEL]; + case ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]; + case ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]; + case ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]; + case ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]; + case ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]; + case ATTRIBUTE_ID_PRESERVE_SIGN_OF_NEGATIVE_ZERO: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO]; default: return 0; } @@ -312,26 +140,24 @@ const bdlat_AttributeInfo *BerEncoderOptions::lookupAttributeInfo(int id) // CREATORS BerEncoderOptions::BerEncoderOptions() -: d_datetimeFractionalSecondPrecision( - DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION) -, d_traceLevel(DEFAULT_INITIALIZER_TRACE_LEVEL) +: d_traceLevel(DEFAULT_INITIALIZER_TRACE_LEVEL) , d_bdeVersionConformance(DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE) +, d_datetimeFractionalSecondPrecision(DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION) , d_encodeEmptyArrays(DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS) -, d_encodeDateAndTimeTypesAsBinary( - DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY) -, d_disableUnselectedChoiceEncoding( - DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING) +, d_encodeDateAndTimeTypesAsBinary(DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY) +, d_disableUnselectedChoiceEncoding(DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING) +, d_preserveSignOfNegativeZero(DEFAULT_INITIALIZER_PRESERVE_SIGN_OF_NEGATIVE_ZERO) { } BerEncoderOptions::BerEncoderOptions(const BerEncoderOptions& original) -: d_datetimeFractionalSecondPrecision( - original.d_datetimeFractionalSecondPrecision) -, d_traceLevel(original.d_traceLevel) +: d_traceLevel(original.d_traceLevel) , d_bdeVersionConformance(original.d_bdeVersionConformance) +, d_datetimeFractionalSecondPrecision(original.d_datetimeFractionalSecondPrecision) , d_encodeEmptyArrays(original.d_encodeEmptyArrays) , d_encodeDateAndTimeTypesAsBinary(original.d_encodeDateAndTimeTypesAsBinary) , d_disableUnselectedChoiceEncoding(original.d_disableUnselectedChoiceEncoding) +, d_preserveSignOfNegativeZero(original.d_preserveSignOfNegativeZero) { } @@ -345,159 +171,78 @@ BerEncoderOptions& BerEncoderOptions::operator=(const BerEncoderOptions& rhs) { if (this != &rhs) { - d_traceLevel = rhs.d_traceLevel; - d_bdeVersionConformance = rhs.d_bdeVersionConformance; - d_encodeEmptyArrays = rhs.d_encodeEmptyArrays; - d_encodeDateAndTimeTypesAsBinary = - rhs.d_encodeDateAndTimeTypesAsBinary; - d_datetimeFractionalSecondPrecision = - rhs.d_datetimeFractionalSecondPrecision; - d_disableUnselectedChoiceEncoding = - rhs.d_disableUnselectedChoiceEncoding; + d_traceLevel = rhs.d_traceLevel; + d_bdeVersionConformance = rhs.d_bdeVersionConformance; + d_encodeEmptyArrays = rhs.d_encodeEmptyArrays; + d_encodeDateAndTimeTypesAsBinary = rhs.d_encodeDateAndTimeTypesAsBinary; + d_datetimeFractionalSecondPrecision = rhs.d_datetimeFractionalSecondPrecision; + d_disableUnselectedChoiceEncoding = rhs.d_disableUnselectedChoiceEncoding; + d_preserveSignOfNegativeZero = rhs.d_preserveSignOfNegativeZero; + } + + return *this; +} + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) \ + && defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) +BerEncoderOptions& +BerEncoderOptions::operator=(BerEncoderOptions&& rhs) +{ + if (this != &rhs) { + d_traceLevel = bsl::move(rhs.d_traceLevel); + d_bdeVersionConformance = bsl::move(rhs.d_bdeVersionConformance); + d_encodeEmptyArrays = bsl::move(rhs.d_encodeEmptyArrays); + d_encodeDateAndTimeTypesAsBinary = bsl::move(rhs.d_encodeDateAndTimeTypesAsBinary); + d_datetimeFractionalSecondPrecision = bsl::move(rhs.d_datetimeFractionalSecondPrecision); + d_disableUnselectedChoiceEncoding = bsl::move(rhs.d_disableUnselectedChoiceEncoding); + d_preserveSignOfNegativeZero = bsl::move(rhs.d_preserveSignOfNegativeZero); } + return *this; } +#endif void BerEncoderOptions::reset() { - d_traceLevel = DEFAULT_INITIALIZER_TRACE_LEVEL; + d_traceLevel = DEFAULT_INITIALIZER_TRACE_LEVEL; d_bdeVersionConformance = DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE; - d_encodeEmptyArrays = DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS; - d_encodeDateAndTimeTypesAsBinary = - DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY; - d_datetimeFractionalSecondPrecision = - DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION; - d_disableUnselectedChoiceEncoding = - DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING; + d_encodeEmptyArrays = DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS; + d_encodeDateAndTimeTypesAsBinary = DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY; + d_datetimeFractionalSecondPrecision = DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION; + d_disableUnselectedChoiceEncoding = DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING; + d_preserveSignOfNegativeZero = DEFAULT_INITIALIZER_PRESERVE_SIGN_OF_NEGATIVE_ZERO; } // ACCESSORS bsl::ostream& BerEncoderOptions::print( - bsl::ostream& stream, - int level, - int spacesPerLevel) const + bsl::ostream& stream, + int level, + int spacesPerLevel) const { - if (level < 0) { - level = -level; - } - else { - bdlb::Print::indent(stream, level, spacesPerLevel); - } - - int levelPlus1 = level + 1; - - if (0 <= spacesPerLevel) { - // multiline - - stream << "[\n"; - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "TraceLevel = "; - bdlb::PrintMethods::print(stream, - d_traceLevel, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "BdeVersionConformance = "; - bdlb::PrintMethods::print(stream, - d_bdeVersionConformance, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "EncodeEmptyArrays = "; - bdlb::PrintMethods::print(stream, - d_encodeEmptyArrays, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "EncodeDateAndTimeTypesAsBinary = "; - bdlb::PrintMethods::print(stream, - d_encodeDateAndTimeTypesAsBinary, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "DatetimeFractionalSecondPrecision = "; - bdlb::PrintMethods::print(stream, - d_datetimeFractionalSecondPrecision, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, levelPlus1, spacesPerLevel); - stream << "DisableUnselectedChoiceEncoding = "; - bdlb::PrintMethods::print(stream, - d_disableUnselectedChoiceEncoding, - -levelPlus1, - spacesPerLevel); - - bdlb::Print::indent(stream, level, spacesPerLevel); - - stream << "]\n"; - } - else { - // single line - - stream << '['; - - stream << ' '; - stream << "TraceLevel = "; - bdlb::PrintMethods::print(stream, d_traceLevel, - -levelPlus1, - spacesPerLevel); - - stream << ' '; - stream << "BdeVersionConformance = "; - bdlb::PrintMethods::print(stream, d_bdeVersionConformance, - -levelPlus1, - spacesPerLevel); - - stream << ' '; - stream << "EncodeEmptyArrays = "; - bdlb::PrintMethods::print(stream, d_encodeEmptyArrays, - -levelPlus1, - spacesPerLevel); - - stream << ' '; - stream << "EncodeDateAndTimeTypesAsBinary = "; - bdlb::PrintMethods::print(stream, d_encodeDateAndTimeTypesAsBinary, - -levelPlus1, - spacesPerLevel); - - stream << ' '; - stream << "DatetimeFractionalSecondPrecision = "; - bdlb::PrintMethods::print(stream, d_datetimeFractionalSecondPrecision, - -levelPlus1, spacesPerLevel); - - stream << ' '; - stream << "DisableUnselectedChoiceEncoding = "; - bdlb::PrintMethods::print(stream, d_disableUnselectedChoiceEncoding, - -levelPlus1, spacesPerLevel); - - stream << " ]"; - } - - return stream << bsl::flush; + bslim::Printer printer(&stream, level, spacesPerLevel); + printer.start(); + printer.printAttribute("traceLevel", this->traceLevel()); + printer.printAttribute("bdeVersionConformance", this->bdeVersionConformance()); + printer.printAttribute("encodeEmptyArrays", this->encodeEmptyArrays()); + printer.printAttribute("encodeDateAndTimeTypesAsBinary", this->encodeDateAndTimeTypesAsBinary()); + printer.printAttribute("datetimeFractionalSecondPrecision", this->datetimeFractionalSecondPrecision()); + printer.printAttribute("disableUnselectedChoiceEncoding", this->disableUnselectedChoiceEncoding()); + printer.printAttribute("preserveSignOfNegativeZero", this->preserveSignOfNegativeZero()); + printer.end(); + return stream; } + } // close package namespace } // close enterprise namespace +// GENERATED BY BLP_BAS_CODEGEN_2021.07.12.1 +// USING bas_codegen.pl -m msg --msgExpand -p balber --noAggregateConversion --noHashSupport -c berencoderoptions balber.xsd // ---------------------------------------------------------------------------- -// Copyright 2015 Bloomberg Finance L.P. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ----------------------------- END-OF-FILE ---------------------------------- +// NOTICE: +// Copyright 2021 Bloomberg Finance L.P. All rights reserved. +// Property of Bloomberg Finance L.P. (BFLP) +// This software is made available solely pursuant to the +// terms of a BFLP license agreement which governs its use. +// ------------------------------- END-OF-FILE -------------------------------- diff --git a/groups/bal/balber/balber_berencoderoptions.h b/groups/bal/balber/balber_berencoderoptions.h index 659147600a..b54f6820d9 100644 --- a/groups/bal/balber/balber_berencoderoptions.h +++ b/groups/bal/balber/balber_berencoderoptions.h @@ -1,28 +1,19 @@ -// balber_berencoderoptions.h-- GENERATED FILE - DO NOT EDIT ---*-C++-*- - -// ---------------------------------------------------------------------------- -// NOTICE -// -// This component is not up to date with current BDE coding standards, and -// should not be used as an example for new development. -// ---------------------------------------------------------------------------- - +// balber_berencoderoptions.h *DO NOT EDIT* @generated -*-C++-*- #ifndef INCLUDED_BALBER_BERENCODEROPTIONS #define INCLUDED_BALBER_BERENCODEROPTIONS #include -BSLS_IDENT_RCSID(bdem_berencoderoptions_h,"$Id$ $CSID$") +BSLS_IDENT_RCSID(balber_berencoderoptions_h,"$Id$ $CSID$") BSLS_IDENT_PRAGMA_ONCE //@PURPOSE: Provide value-semantic attribute classes -// -//@CLASSES: -// balber::BerEncoderOptions: BER encoding options -// -//@DESCRIPTION: TBD + +#include #include + #include + #include #include @@ -33,8 +24,11 @@ BSLS_IDENT_PRAGMA_ONCE #include #include +#include namespace BloombergLP { + +namespace balber { class BerEncoderOptions; } namespace balber { // ======================= @@ -44,130 +38,113 @@ namespace balber { class BerEncoderOptions { // BER encoding options - // DATA - int d_datetimeFractionalSecondPrecision; - // This option controls the number of decimal places used for seconds - // when encoding 'Datetime' and 'DatetimeTz'. - - int d_traceLevel; + // INSTANCE DATA + int d_traceLevel; // trace (verbosity) level - - int d_bdeVersionConformance; + int d_bdeVersionConformance; // The largest BDE version that can be assumed of the corresponding - // decoder for the encoded message, expressed as - // '10000*majorVersion + 100*minorVersion + patchVersion' (e.g., 1.5.0 - // is expressed as 10500). Ideally, the BER encoder should be - // permitted to generate any BER that conforms to X.690 (Basic Encoding - // Rules) and X.694 (mapping of XSD to ASN.1). In practice, however, - // certain unimplemented features and missunderstandings of these - // standards have resulted in a decoder that cannot accept the full - // range of legal inputs. Even when the encoder and decoder are both - // upgraded to a richer subset of BER, the program receiving the - // encoded values may not have been recompiled with the latest version - // and, thus restricting the encoder to emit BER that can be understood - // by the decoder at the other end of the wire. If it is that the - // receiver has a more modern decoder, set this variable to a larger - // value to allow the encoder to produce BER that is richer and more - // standards conformant. The default should be increased only when old - // copies of the decoder are completely out of circulation. - - bool d_encodeEmptyArrays; + // decoder for the encoded message, expressed as 10000*majorVersion + + // 100*minorVersion + patchVersion (e.g. 1.5.0 is expressed as 10500). + // + // Ideally, the BER encoder should be permitted to generate any BER + // that conforms to X.690 (Basic Encoding Rules) and X.694 (mapping of + // XSD to ASN.1). In practice, however, certain unimplemented features + // and missunderstandings of these standards have resulted in a decoder + // that cannot accept the full range of legal inputs. Even when the + // encoder and decoder are both upgraded to a richer subset of BER, the + // program receiving the encoded values may not have been recompiled + // with the latest version and, thus restricting the encoder to emit + // BER that can be understood by the decoder at the other end of the + // wire. If it is that the receiver has a more modern decoder, set + // this variable to a larger value to allow the encoder to produce BER + // that is richer and more standards conformant. The default should be + // increased only when old copies of the decoder are completely out of + // circulation. + int d_datetimeFractionalSecondPrecision; + // This option controls the number of decimal places used for seconds + // when encoding 'Datetime' and 'DatetimeTz'. + bool d_encodeEmptyArrays; // This option allows users to control if empty arrays are encoded. By // default empty arrays are encoded as not encoding empty arrays is // non-compliant with the BER encoding specification. - - bool d_encodeDateAndTimeTypesAsBinary; + bool d_encodeDateAndTimeTypesAsBinary; // This option allows users to control if date and time types are // encoded as binary integers. By default these types are encoded as // strings in the ISO 8601 format. - - bool d_disableUnselectedChoiceEncoding; - // This encode option allows users to control if it is an error to - // try and encoded any element with an unselected choice. By default - // the encoder allows unselected choice by eliding from the encoding. + bool d_disableUnselectedChoiceEncoding; + // This encode option allows users to control if it is an error to try + // and encoded any element with an unselected choice. By default the + // encoder allows unselected choices by eliding them from the encoding. + bool d_preserveSignOfNegativeZero; + // This *backward*-*compatibility* option controls whether or not + // negative zero floating-point values are encoded as the BER + // representation of negative zero or positive zero. If this option is + // 'true', negative zero floating-point values are encoded as the BER + // representation of negative zero, such that they will decode back to + // negative zero. If this option is 'false', negative zero + // floating-point values are encoded as the BER representation of + // positive zero, such they they will decode to positive zero. For + // backward-compatibility purposes, the default value of this option is + // 'false'. Setting this option to 'true' requires the receiving + // decoder to come from BDE release '3.90.x' or later, or otherwise + // comply to the ISO/IEC 8825-1:2015 standard. Clients are encouraged + // to update their recipients to the latest version of BDE and set this + // option to 'true'. Note that the 'false' value of this option will + // eventually be deprecated, and the default value changed to 'true'. public: // TYPES enum { - e_ATTRIBUTE_ID_TRACE_LEVEL = 0 - , e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE = 1 - , e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS = 2 - , e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = 3 - , e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION = 4 - , e_ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING = 5 -#ifndef BDE_OMIT_INTERNAL_DEPRECATED - , ATTRIBUTE_ID_TRACE_LEVEL = - e_ATTRIBUTE_ID_TRACE_LEVEL - , ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE = - e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE - , ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS = - e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS - , ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = - e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY - , ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION = - e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION - -#endif // BDE_OMIT_INTERNAL_DEPRECATED + ATTRIBUTE_ID_TRACE_LEVEL = 0 + , ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE = 1 + , ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS = 2 + , ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = 3 + , ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION = 4 + , ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING = 5 + , ATTRIBUTE_ID_PRESERVE_SIGN_OF_NEGATIVE_ZERO = 6 }; enum { - k_NUM_ATTRIBUTES = 6 -#ifndef BDE_OMIT_INTERNAL_DEPRECATED - , NUM_ATTRIBUTES = k_NUM_ATTRIBUTES -#endif // BDE_OMIT_INTERNAL_DEPRECATED + NUM_ATTRIBUTES = 7 }; enum { - e_ATTRIBUTE_INDEX_TRACE_LEVEL = 0 - , e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE = 1 - , e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS = 2 - , e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = 3 - , e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION = 4 - , e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING = 5 -#ifndef BDE_OMIT_INTERNAL_DEPRECATED - , ATTRIBUTE_INDEX_TRACE_LEVEL = - e_ATTRIBUTE_INDEX_TRACE_LEVEL - , ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE = - e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE - , ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS = - e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS - , ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY - , ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION = - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION -#endif // BDE_OMIT_INTERNAL_DEPRECATED + ATTRIBUTE_INDEX_TRACE_LEVEL = 0 + , ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE = 1 + , ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS = 2 + , ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = 3 + , ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION = 4 + , ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING = 5 + , ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO = 6 }; // CONSTANTS static const char CLASS_NAME[]; - static const int DEFAULT_INITIALIZER_TRACE_LEVEL; - static const int DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE; + + static const int DEFAULT_INITIALIZER_TRACE_LEVEL; + + static const int DEFAULT_INITIALIZER_BDE_VERSION_CONFORMANCE; + static const bool DEFAULT_INITIALIZER_ENCODE_EMPTY_ARRAYS; + static const bool DEFAULT_INITIALIZER_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY; - static const int DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION; + + static const int DEFAULT_INITIALIZER_DATETIME_FRACTIONAL_SECOND_PRECISION; + static const bool DEFAULT_INITIALIZER_DISABLE_UNSELECTED_CHOICE_ENCODING; + + static const bool DEFAULT_INITIALIZER_PRESERVE_SIGN_OF_NEGATIVE_ZERO; + static const bdlat_AttributeInfo ATTRIBUTE_INFO_ARRAY[]; public: // CLASS METHODS - static int maxSupportedBdexVersion(int versionSelector); - // Return the maximum valid BDEX format version, as indicated by the - // specified 'versionSelector', to be passed to the 'bdexStreamOut' - // method. Note that the 'versionSelector' is expected to be formatted - // as 'yyyymmdd', a date representation. See the 'bslx' package-level - // documentation for more information on BDEX streaming of - // value-semantic types and containers. - -#ifndef BDE_OMIT_INTERNAL_DEPRECATED - static int maxSupportedBdexVersion(); - // Return the most current BDEX streaming version number supported by + // Return the most current 'bdex' streaming version number supported by // this class. See the 'bslx' package-level documentation for more - // information on BDEX streaming of value-semantic types and + // information on 'bdex' streaming of value-semantic types and // containers. -#endif // BDE_OMIT_INTERNAL_DEPRECATED - static const bdlat_AttributeInfo *lookupAttributeInfo(int id); // Return attribute information for the attribute indicated by the // specified 'id' if the attribute exists, and 0 otherwise. @@ -188,6 +165,14 @@ class BerEncoderOptions { // Create an object of type 'BerEncoderOptions' having the value of the // specified 'original' object. +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) \ + && defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) + BerEncoderOptions(BerEncoderOptions&& original) = default; + // Create an object of type 'BerEncoderOptions' having the value of the + // specified 'original' object. After performing this action, the + // 'original' object will be left in a valid, but unspecified state. +#endif + ~BerEncoderOptions(); // Destroy this object. @@ -195,82 +180,87 @@ class BerEncoderOptions { BerEncoderOptions& operator=(const BerEncoderOptions& rhs); // Assign to this object the value of the specified 'rhs' object. +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) \ + && defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) + BerEncoderOptions& operator=(BerEncoderOptions&& rhs); + // Assign to this object the value of the specified 'rhs' object. + // After performing this action, the 'rhs' object will be left in a + // valid, but unspecified state. +#endif + template STREAM& bdexStreamIn(STREAM& stream, int version); // Assign to this object the value read from the specified input - // 'stream' using the specified 'version' format, and return a - // reference to 'stream'. If 'stream' is initially invalid, this - // operation has no effect. If 'version' is not supported, this object - // is unaltered and 'stream' is invalidated, but otherwise unmodified. - // If 'version' is supported but 'stream' becomes invalid during this - // operation, this object has an undefined, but valid, state. Note - // that no version is read from 'stream'. See the 'bslx' package-level - // documentation for more information on BDEX streaming of - // value-semantic types and containers. + // 'stream' using the specified 'version' format and return a reference + // to the modifiable 'stream'. If 'stream' is initially invalid, this + // operation has no effect. If 'stream' becomes invalid during this + // operation, this object is valid, but its value is undefined. If + // 'version' is not supported, 'stream' is marked invalid and this + // object is unaltered. Note that no version is read from 'stream'. + // See the 'bslx' package-level documentation for more information on + // 'bdex' streaming of value-semantic types and containers. void reset(); - // Reset this object to the default value (i.e., its value upon default - // construction). + // Reset this object to the default value (i.e., its value upon + // default construction). template int manipulateAttributes(MANIPULATOR& manipulator); // Invoke the specified 'manipulator' sequentially on the address of // each (modifiable) attribute of this object, supplying 'manipulator' // with the corresponding attribute information structure until such - // invocation returns a non-zero value. Return the value from the last - // invocation of 'manipulator' (i.e., the invocation that terminated - // the sequence). + // invocation returns a non-zero value. Return the value from the + // last invocation of 'manipulator' (i.e., the invocation that + // terminated the sequence). template int manipulateAttribute(MANIPULATOR& manipulator, int id); - // Invoke the specified 'manipulator' on the address of the - // (modifiable) attribute indicated by the specified 'id', supplying - // 'manipulator' with the corresponding attribute information - // structure. Return the value returned from the invocation of - // 'manipulator' if 'id' identifies an attribute of this class, and -1 - // otherwise. + // Invoke the specified 'manipulator' on the address of + // the (modifiable) attribute indicated by the specified 'id', + // supplying 'manipulator' with the corresponding attribute + // information structure. Return the value returned from the + // invocation of 'manipulator' if 'id' identifies an attribute of this + // class, and -1 otherwise. template int manipulateAttribute(MANIPULATOR& manipulator, const char *name, int nameLength); - // Invoke the specified 'manipulator' on the address of the - // (modifiable) attribute indicated by the specified 'name' of the + // Invoke the specified 'manipulator' on the address of + // the (modifiable) attribute indicated by the specified 'name' of the // specified 'nameLength', supplying 'manipulator' with the // corresponding attribute information structure. Return the value // returned from the invocation of 'manipulator' if 'name' identifies // an attribute of this class, and -1 otherwise. void setTraceLevel(int value); - // Set the 'TraceLevel' attribute of this object to the specified + // Set the "TraceLevel" attribute of this object to the specified // 'value'. int& bdeVersionConformance(); - // Return a reference to the modifiable 'BdeVersionConformance' + // Return a reference to the modifiable "BdeVersionConformance" // attribute of this object. void setEncodeEmptyArrays(bool value); - // Set the 'EncodeEmptyArrays' attribute of this object to the + // Set the "EncodeEmptyArrays" attribute of this object to the // specified 'value'. void setEncodeDateAndTimeTypesAsBinary(bool value); - // Set the 'EncodeDateAndTimeTypesAsBinary' attribute of this object to - // the specified 'value'. If this option is set to 'true' then date - // and time types will be encoded (in a standard-incompliant way) as an - // octet string as opposed to as a string in the ISO 8601 format as - // required by the standard. Note that the binary encoding format is - // incompatible with the string encoding format and must be used after - // ensuring that the ber decoder can decode the binary format. + // Set the "EncodeDateAndTimeTypesAsBinary" attribute of this object to + // the specified 'value'. void setDatetimeFractionalSecondPrecision(int value); - // Set the 'DatetimeFractionalSecondPrecision' attribute of this object - // to the specified 'value'. The behavior is undefined unless - // 'value == 3 || value == 6'. + // Set the "DatetimeFractionalSecondPrecision" attribute of this object + // to the specified 'value'. void setDisableUnselectedChoiceEncoding(bool value); - // Set the 'DisableUnselectedChoiceEncoding' attribute of this object + // Set the "DisableUnselectedChoiceEncoding" attribute of this object // to the specified 'value'. + void setPreserveSignOfNegativeZero(bool value); + // Set the "PreserveSignOfNegativeZero" attribute of this object to the + // specified 'value'. + // ACCESSORS bsl::ostream& print(bsl::ostream& stream, int level = 0, @@ -289,28 +279,26 @@ class BerEncoderOptions { template STREAM& bdexStreamOut(STREAM& stream, int version) const; - // Write the value of this object, using the specified 'version' - // format, to the specified output 'stream', and return a reference to - // 'stream'. If 'stream' is initially invalid, this operation has no - // effect. If 'version' is not supported, 'stream' is invalidated, but - // otherwise unmodified. Note that 'version' is not written to - // 'stream'. See the 'bslx' package-level documentation for more - // information on BDEX streaming of value-semantic types and - // containers. + // Write the value of this object to the specified output 'stream' + // using the specified 'version' format and return a reference to the + // modifiable 'stream'. If 'version' is not supported, 'stream' is + // unmodified. Note that 'version' is not written to 'stream'. + // See the 'bslx' package-level documentation for more information + // on 'bdex' streaming of value-semantic types and containers. template int accessAttributes(ACCESSOR& accessor) const; // Invoke the specified 'accessor' sequentially on each - // (non-modifiable) attribute of this object, supplying 'accessor' with - // the corresponding attribute information structure until such - // invocation returns a non-zero value. Return the value from the last - // invocation of 'accessor' (i.e., the invocation that terminated the - // sequence). + // (non-modifiable) attribute of this object, supplying 'accessor' + // with the corresponding attribute information structure until such + // invocation returns a non-zero value. Return the value from the + // last invocation of 'accessor' (i.e., the invocation that terminated + // the sequence). template int accessAttribute(ACCESSOR& accessor, int id) const; - // Invoke the specified 'accessor' on the (non-modifiable) attribute of - // this object indicated by the specified 'id', supplying 'accessor' + // Invoke the specified 'accessor' on the (non-modifiable) attribute + // of this object indicated by the specified 'id', supplying 'accessor' // with the corresponding attribute information structure. Return the // value returned from the invocation of 'accessor' if 'id' identifies // an attribute of this class, and -1 otherwise. @@ -319,36 +307,39 @@ class BerEncoderOptions { int accessAttribute(ACCESSOR& accessor, const char *name, int nameLength) const; - // Invoke the specified 'accessor' on the (non-modifiable) attribute of - // this object indicated by the specified 'name' of the specified + // Invoke the specified 'accessor' on the (non-modifiable) attribute + // of this object indicated by the specified 'name' of the specified // 'nameLength', supplying 'accessor' with the corresponding attribute // information structure. Return the value returned from the // invocation of 'accessor' if 'name' identifies an attribute of this // class, and -1 otherwise. int traceLevel() const; - // Return a reference to the non-modifiable 'TraceLevel' attribute of - // this object. + // Return the value of the "TraceLevel" attribute of this object. int bdeVersionConformance() const; - // Return a reference to the non-modifiable 'BdeVersionConformance' - // attribute of this object. + // Return the value of the "BdeVersionConformance" attribute of this + // object. bool encodeEmptyArrays() const; - // Return a reference to the non-modifiable 'EncodeEmptyArrays' - // attribute of this object. + // Return the value of the "EncodeEmptyArrays" attribute of this + // object. bool encodeDateAndTimeTypesAsBinary() const; - // Return a reference to the non-modifiable - // 'EncodeDateAndTimeTypesAsBinary' attribute of this object. + // Return the value of the "EncodeDateAndTimeTypesAsBinary" attribute + // of this object. int datetimeFractionalSecondPrecision() const; - // Return a reference to the non-modifiable - // 'DatetimeFractionalSecondPrecision' attribute of this object. + // Return the value of the "DatetimeFractionalSecondPrecision" + // attribute of this object. bool disableUnselectedChoiceEncoding() const; - // Return the value of the non-modifiable - // 'DatetimeFractionalSecondPrecision' attribute of this object. + // Return the value of the "DisableUnselectedChoiceEncoding" attribute + // of this object. + + bool preserveSignOfNegativeZero() const; + // Return the value of the "PreserveSignOfNegativeZero" attribute of + // this object. }; // FREE OPERATORS @@ -367,16 +358,17 @@ bool operator!=(const BerEncoderOptions& lhs, const BerEncoderOptions& rhs); inline bsl::ostream& operator<<(bsl::ostream& stream, const BerEncoderOptions& rhs); - // Format the specified 'rhs' to the specified output 'stream' and return a - // reference to the modifiable 'stream'. + // Format the specified 'rhs' to the specified output 'stream' and + // return a reference to the modifiable 'stream'. } // close package namespace // TRAITS + BDLAT_DECL_SEQUENCE_WITH_BITWISEMOVEABLE_TRAITS(balber::BerEncoderOptions) // ============================================================================ -// INLINE FUNCTION DEFINITIONS +// INLINE FUNCTION DEFINITIONS // ============================================================================ namespace balber { @@ -387,18 +379,10 @@ namespace balber { // CLASS METHODS inline -int BerEncoderOptions::maxSupportedBdexVersion(int /* versionSelector */) -{ - return 1; // versions start at 1. -} - -#ifndef BDE_OMIT_INTERNAL_DEPRECATED -inline int BerEncoderOptions::maxSupportedBdexVersion() { - return maxSupportedBdexVersion(0); + return 2; // versions start at 1. } -#endif // BDE_OMIT_INTERNAL_DEPRECATED // MANIPULATORS template @@ -406,28 +390,24 @@ STREAM& BerEncoderOptions::bdexStreamIn(STREAM& stream, int version) { if (stream) { switch (version) { + case 2: { + bslx::InStreamFunctions::bdexStreamIn(stream, d_traceLevel, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_bdeVersionConformance, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_encodeEmptyArrays, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_encodeDateAndTimeTypesAsBinary, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_datetimeFractionalSecondPrecision, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_disableUnselectedChoiceEncoding, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_preserveSignOfNegativeZero, 1); + } break; case 1: { - bslx::InStreamFunctions::bdexStreamIn(stream, - d_traceLevel, - 1); - bslx::InStreamFunctions::bdexStreamIn(stream, - d_bdeVersionConformance, - 1); - bslx::InStreamFunctions::bdexStreamIn(stream, - d_encodeEmptyArrays, - 1); - bslx::InStreamFunctions::bdexStreamIn( - stream, - d_encodeDateAndTimeTypesAsBinary, - 1); - bslx::InStreamFunctions::bdexStreamIn( - stream, - d_datetimeFractionalSecondPrecision, - 1); - bslx::InStreamFunctions::bdexStreamIn( - stream, - d_disableUnselectedChoiceEncoding, - 1); + reset(); + + bslx::InStreamFunctions::bdexStreamIn(stream, d_traceLevel, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_bdeVersionConformance, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_encodeEmptyArrays, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_encodeDateAndTimeTypesAsBinary, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_datetimeFractionalSecondPrecision, 1); + bslx::InStreamFunctions::bdexStreamIn(stream, d_disableUnselectedChoiceEncoding, 1); } break; default: { stream.invalidate(); @@ -442,43 +422,37 @@ int BerEncoderOptions::manipulateAttributes(MANIPULATOR& manipulator) { int ret; - ret = manipulator(&d_traceLevel, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]); + ret = manipulator(&d_traceLevel, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TRACE_LEVEL]); if (ret) { - return ret; // RETURN + return ret; + } + + ret = manipulator(&d_bdeVersionConformance, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); + if (ret) { + return ret; } - ret = manipulator( - &d_bdeVersionConformance, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); + ret = manipulator(&d_encodeEmptyArrays, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); if (ret) { - return ret; // RETURN + return ret; } - ret = manipulator( - &d_encodeEmptyArrays, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); + ret = manipulator(&d_encodeDateAndTimeTypesAsBinary, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); if (ret) { - return ret; // RETURN + return ret; } - ret = manipulator(&d_encodeDateAndTimeTypesAsBinary, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); + ret = manipulator(&d_datetimeFractionalSecondPrecision, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); if (ret) { - return ret; // RETURN + return ret; } - ret = manipulator(&d_datetimeFractionalSecondPrecision, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); + ret = manipulator(&d_disableUnselectedChoiceEncoding, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); if (ret) { return ret; } - ret = manipulator(&d_disableUnselectedChoiceEncoding, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); + ret = manipulator(&d_preserveSignOfNegativeZero, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO]); if (ret) { return ret; } @@ -489,58 +463,47 @@ int BerEncoderOptions::manipulateAttributes(MANIPULATOR& manipulator) template int BerEncoderOptions::manipulateAttribute(MANIPULATOR& manipulator, int id) { - enum { k_NOT_FOUND = -1 }; + enum { NOT_FOUND = -1 }; switch (id) { - case e_ATTRIBUTE_ID_TRACE_LEVEL: { - return manipulator( - &d_traceLevel, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]); - } break; - case e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: { - return manipulator( - &d_bdeVersionConformance, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); - } break; - case e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: { - return manipulator( - &d_encodeEmptyArrays, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); - } break; - case e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: { - return manipulator( - &d_encodeDateAndTimeTypesAsBinary, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); - } break; - case e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: { - return manipulator( - &d_datetimeFractionalSecondPrecision, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); - } break; - case e_ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: { - return manipulator( - &d_disableUnselectedChoiceEncoding, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); - } break; + case ATTRIBUTE_ID_TRACE_LEVEL: { + return manipulator(&d_traceLevel, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TRACE_LEVEL]); + } + case ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: { + return manipulator(&d_bdeVersionConformance, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); + } + case ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: { + return manipulator(&d_encodeEmptyArrays, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); + } + case ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: { + return manipulator(&d_encodeDateAndTimeTypesAsBinary, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); + } + case ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: { + return manipulator(&d_datetimeFractionalSecondPrecision, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); + } + case ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: { + return manipulator(&d_disableUnselectedChoiceEncoding, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); + } + case ATTRIBUTE_ID_PRESERVE_SIGN_OF_NEGATIVE_ZERO: { + return manipulator(&d_preserveSignOfNegativeZero, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO]); + } default: - return k_NOT_FOUND; + return NOT_FOUND; } } template -int BerEncoderOptions::manipulateAttribute(MANIPULATOR& manipulator, - const char *name, - int nameLength) +int BerEncoderOptions::manipulateAttribute( + MANIPULATOR& manipulator, + const char *name, + int nameLength) { - enum { k_NOT_FOUND = -1 }; + enum { NOT_FOUND = -1 }; - const bdlat_AttributeInfo *attributeInfo = lookupAttributeInfo(name, - nameLength); - if (!attributeInfo) { - return k_NOT_FOUND; + const bdlat_AttributeInfo *attributeInfo = + lookupAttributeInfo(name, nameLength); + if (0 == attributeInfo) { + return NOT_FOUND; } return manipulateAttribute(manipulator, attributeInfo->d_id); @@ -573,7 +536,6 @@ void BerEncoderOptions::setEncodeDateAndTimeTypesAsBinary(bool value) inline void BerEncoderOptions::setDatetimeFractionalSecondPrecision(int value) { - BSLS_ASSERT(value == 3 || value == 6); d_datetimeFractionalSecondPrecision = value; } @@ -583,37 +545,37 @@ void BerEncoderOptions::setDisableUnselectedChoiceEncoding(bool value) d_disableUnselectedChoiceEncoding = value; } +inline +void BerEncoderOptions::setPreserveSignOfNegativeZero(bool value) +{ + d_preserveSignOfNegativeZero = value; +} + // ACCESSORS template STREAM& BerEncoderOptions::bdexStreamOut(STREAM& stream, int version) const { switch (version) { + case 2: { + bslx::OutStreamFunctions::bdexStreamOut(stream, this->traceLevel(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->bdeVersionConformance(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->encodeEmptyArrays(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->encodeDateAndTimeTypesAsBinary(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->datetimeFractionalSecondPrecision(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->disableUnselectedChoiceEncoding(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->preserveSignOfNegativeZero(), 1); + } break; case 1: { - bslx::OutStreamFunctions::bdexStreamOut(stream, - d_traceLevel, - 1); - bslx::OutStreamFunctions::bdexStreamOut(stream, - d_bdeVersionConformance, - 1); - bslx::OutStreamFunctions::bdexStreamOut(stream, - d_encodeEmptyArrays, - 1); - bslx::OutStreamFunctions::bdexStreamOut( - stream, - d_encodeDateAndTimeTypesAsBinary, - 1); - bslx::OutStreamFunctions::bdexStreamOut( - stream, - d_datetimeFractionalSecondPrecision, - 1); - bslx::OutStreamFunctions::bdexStreamOut( - stream, - d_disableUnselectedChoiceEncoding, - 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->traceLevel(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->bdeVersionConformance(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->encodeEmptyArrays(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->encodeDateAndTimeTypesAsBinary(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->datetimeFractionalSecondPrecision(), 1); + bslx::OutStreamFunctions::bdexStreamOut(stream, this->disableUnselectedChoiceEncoding(), 1); } break; default: { stream.invalidate(); - } break; + } } return stream; } @@ -623,47 +585,39 @@ int BerEncoderOptions::accessAttributes(ACCESSOR& accessor) const { int ret; - ret = accessor(d_traceLevel, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]); + ret = accessor(d_traceLevel, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TRACE_LEVEL]); if (ret) { - return ret; // RETURN + return ret; } - ret = accessor(d_bdeVersionConformance, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); + ret = accessor(d_bdeVersionConformance, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); if (ret) { - return ret; // RETURN + return ret; } - ret = accessor(d_encodeEmptyArrays, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); + ret = accessor(d_encodeEmptyArrays, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); if (ret) { - return ret; // RETURN + return ret; } - ret = accessor(d_encodeDateAndTimeTypesAsBinary, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); + ret = accessor(d_encodeDateAndTimeTypesAsBinary, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); if (ret) { - return ret; // RETURN + return ret; } - ret = accessor(d_datetimeFractionalSecondPrecision, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); - + ret = accessor(d_datetimeFractionalSecondPrecision, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); if (ret) { - return ret; // RETURN + return ret; } - ret = accessor(d_disableUnselectedChoiceEncoding, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); + ret = accessor(d_disableUnselectedChoiceEncoding, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); + if (ret) { + return ret; + } + ret = accessor(d_preserveSignOfNegativeZero, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO]); if (ret) { - return ret; // RETURN + return ret; } return ret; @@ -672,57 +626,47 @@ int BerEncoderOptions::accessAttributes(ACCESSOR& accessor) const template int BerEncoderOptions::accessAttribute(ACCESSOR& accessor, int id) const { - enum { k_NOT_FOUND = -1 }; + enum { NOT_FOUND = -1 }; switch (id) { - case e_ATTRIBUTE_ID_TRACE_LEVEL: { - return accessor(d_traceLevel, - ATTRIBUTE_INFO_ARRAY[e_ATTRIBUTE_INDEX_TRACE_LEVEL]); - } break; - case e_ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: { - return accessor(d_bdeVersionConformance, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); - } break; - case e_ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: { - return accessor(d_encodeEmptyArrays, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); - } break; - case e_ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: { - return accessor( - d_encodeDateAndTimeTypesAsBinary, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); - } break; - case e_ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: { - return accessor( - d_datetimeFractionalSecondPrecision, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); - } break; - case e_ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: { - return accessor( - d_disableUnselectedChoiceEncoding, - ATTRIBUTE_INFO_ARRAY[ - e_ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); - } break; + case ATTRIBUTE_ID_TRACE_LEVEL: { + return accessor(d_traceLevel, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TRACE_LEVEL]); + } + case ATTRIBUTE_ID_BDE_VERSION_CONFORMANCE: { + return accessor(d_bdeVersionConformance, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BDE_VERSION_CONFORMANCE]); + } + case ATTRIBUTE_ID_ENCODE_EMPTY_ARRAYS: { + return accessor(d_encodeEmptyArrays, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_EMPTY_ARRAYS]); + } + case ATTRIBUTE_ID_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY: { + return accessor(d_encodeDateAndTimeTypesAsBinary, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ENCODE_DATE_AND_TIME_TYPES_AS_BINARY]); + } + case ATTRIBUTE_ID_DATETIME_FRACTIONAL_SECOND_PRECISION: { + return accessor(d_datetimeFractionalSecondPrecision, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DATETIME_FRACTIONAL_SECOND_PRECISION]); + } + case ATTRIBUTE_ID_DISABLE_UNSELECTED_CHOICE_ENCODING: { + return accessor(d_disableUnselectedChoiceEncoding, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_DISABLE_UNSELECTED_CHOICE_ENCODING]); + } + case ATTRIBUTE_ID_PRESERVE_SIGN_OF_NEGATIVE_ZERO: { + return accessor(d_preserveSignOfNegativeZero, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PRESERVE_SIGN_OF_NEGATIVE_ZERO]); + } default: - return k_NOT_FOUND; + return NOT_FOUND; } } template -int BerEncoderOptions::accessAttribute(ACCESSOR& accessor, - const char *name, - int nameLength) const +int BerEncoderOptions::accessAttribute( + ACCESSOR& accessor, + const char *name, + int nameLength) const { - enum { k_NOT_FOUND = -1 }; + enum { NOT_FOUND = -1 }; - const bdlat_AttributeInfo *attributeInfo = lookupAttributeInfo(name, - nameLength); - if (!attributeInfo) { - return k_NOT_FOUND; + const bdlat_AttributeInfo *attributeInfo = + lookupAttributeInfo(name, nameLength); + if (0 == attributeInfo) { + return NOT_FOUND; } return accessAttribute(accessor, attributeInfo->d_id); @@ -764,62 +708,55 @@ bool BerEncoderOptions::disableUnselectedChoiceEncoding() const return d_disableUnselectedChoiceEncoding; } +inline +bool BerEncoderOptions::preserveSignOfNegativeZero() const +{ + return d_preserveSignOfNegativeZero; +} + } // close package namespace // FREE FUNCTIONS + inline -bool balber::operator==(const BerEncoderOptions& lhs, - const BerEncoderOptions& rhs) +bool balber::operator==( + const balber::BerEncoderOptions& lhs, + const balber::BerEncoderOptions& rhs) { - return lhs.traceLevel() == rhs.traceLevel() - && lhs.bdeVersionConformance() == rhs.bdeVersionConformance() - && lhs.encodeEmptyArrays() == rhs.encodeEmptyArrays() - && lhs.encodeDateAndTimeTypesAsBinary() == - rhs.encodeDateAndTimeTypesAsBinary() - && lhs.datetimeFractionalSecondPrecision() == - rhs.datetimeFractionalSecondPrecision() - && lhs.disableUnselectedChoiceEncoding() == - rhs.disableUnselectedChoiceEncoding(); + return lhs.traceLevel() == rhs.traceLevel() + && lhs.bdeVersionConformance() == rhs.bdeVersionConformance() + && lhs.encodeEmptyArrays() == rhs.encodeEmptyArrays() + && lhs.encodeDateAndTimeTypesAsBinary() == rhs.encodeDateAndTimeTypesAsBinary() + && lhs.datetimeFractionalSecondPrecision() == rhs.datetimeFractionalSecondPrecision() + && lhs.disableUnselectedChoiceEncoding() == rhs.disableUnselectedChoiceEncoding() + && lhs.preserveSignOfNegativeZero() == rhs.preserveSignOfNegativeZero(); } inline -bool balber::operator!=(const BerEncoderOptions& lhs, - const BerEncoderOptions& rhs) +bool balber::operator!=( + const balber::BerEncoderOptions& lhs, + const balber::BerEncoderOptions& rhs) { - return lhs.traceLevel() != rhs.traceLevel() - || lhs.bdeVersionConformance() != rhs.bdeVersionConformance() - || lhs.encodeEmptyArrays() != rhs.encodeEmptyArrays() - || lhs.encodeDateAndTimeTypesAsBinary() != - rhs.encodeDateAndTimeTypesAsBinary() - || lhs.datetimeFractionalSecondPrecision() != - rhs.datetimeFractionalSecondPrecision() - || lhs.disableUnselectedChoiceEncoding() != - rhs.disableUnselectedChoiceEncoding(); + return !(lhs == rhs); } inline -bsl::ostream& balber::operator<<(bsl::ostream& stream, - const BerEncoderOptions& rhs) +bsl::ostream& balber::operator<<( + bsl::ostream& stream, + const balber::BerEncoderOptions& rhs) { return rhs.print(stream, 0, -1); } } // close enterprise namespace - #endif +// GENERATED BY BLP_BAS_CODEGEN_2021.07.12.1 +// USING bas_codegen.pl -m msg --msgExpand -p balber --noAggregateConversion --noHashSupport -c berencoderoptions balber.xsd // ---------------------------------------------------------------------------- -// Copyright 2018 Bloomberg Finance L.P. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// ----------------------------- END-OF-FILE ---------------------------------- +// NOTICE: +// Copyright 2021 Bloomberg Finance L.P. All rights reserved. +// Property of Bloomberg Finance L.P. (BFLP) +// This software is made available solely pursuant to the +// terms of a BFLP license agreement which governs its use. +// ------------------------------- END-OF-FILE -------------------------------- diff --git a/groups/bal/balber/balber_berencoderoptions.t.cpp b/groups/bal/balber/balber_berencoderoptions.t.cpp index 4e2ec5bf55..4dcab0339a 100644 --- a/groups/bal/balber/balber_berencoderoptions.t.cpp +++ b/groups/bal/balber/balber_berencoderoptions.t.cpp @@ -705,6 +705,7 @@ int main(int argc, char *argv[]) const bool D4 = false; // 'encodeDateAndTimeTypesAsBinary' const int D5 = 3; // 'datetimeFractionalSecondPrecision' const int D6 = false; // 'disableUnselectedChoiceEncoding' + const bool D7 = false; // 'preserveSignOfNegativeZero' if (verbose) cout << "Create an object using the default constructor." << endl; @@ -726,6 +727,8 @@ int main(int argc, char *argv[]) D5 == X.datetimeFractionalSecondPrecision()); LOOP2_ASSERT(D6, X.disableUnselectedChoiceEncoding(), D6 == X.disableUnselectedChoiceEncoding()); + LOOP2_ASSERT(D7, X.preserveSignOfNegativeZero(), + D7 == X.preserveSignOfNegativeZero()); } break; case 1: { // -------------------------------------------------------------------- @@ -763,6 +766,7 @@ int main(int argc, char *argv[]) typedef bool T4; // 'encodeDateAndTimeTypesAsBinary' typedef int T5; // 'datetimeFractionalSecondPrecision' typedef int T6; // 'disableUnselectedChoiceEncoding' + typedef bool T7; // 'preserveSignOfNegativeZero' // Attribute 1 Values: 'traceLevel' @@ -794,6 +798,11 @@ int main(int argc, char *argv[]) const T6 D6 = false; // default value const T6 A6 = true; + // Attribute 7 Values: 'preserveSignOfNegativeZero' + + const T7 D7 = false; // default value + const T7 A7 = true; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (verbose) cout << "\n 1. Create an object 'w' (default ctor)." @@ -810,6 +819,7 @@ int main(int argc, char *argv[]) ASSERT(D4 == W.encodeDateAndTimeTypesAsBinary()); ASSERT(D5 == W.datetimeFractionalSecondPrecision()); ASSERT(D6 == W.disableUnselectedChoiceEncoding()); + ASSERT(D7 == W.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'w' 'w'." << endl; @@ -832,6 +842,7 @@ int main(int argc, char *argv[]) ASSERT(D4 == X.encodeDateAndTimeTypesAsBinary()); ASSERT(D5 == X.datetimeFractionalSecondPrecision()); ASSERT(D6 == X.disableUnselectedChoiceEncoding()); + ASSERT(D7 == X.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'x' 'w', 'x'." << endl; @@ -850,6 +861,7 @@ int main(int argc, char *argv[]) mX.setEncodeDateAndTimeTypesAsBinary(A4); mX.setDatetimeFractionalSecondPrecision(A5); mX.setDisableUnselectedChoiceEncoding(A6); + mX.setPreserveSignOfNegativeZero(A7); if (veryVerbose) cout << "\ta. Check new value of 'x'." << endl; if (veryVeryVerbose) { T_ T_ P(X) } @@ -860,6 +872,7 @@ int main(int argc, char *argv[]) ASSERT(A4 == X.encodeDateAndTimeTypesAsBinary()); ASSERT(A5 == X.datetimeFractionalSecondPrecision()); ASSERT(A6 == X.disableUnselectedChoiceEncoding()); + ASSERT(A7 == X.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'x' 'w', 'x'." << endl; @@ -879,6 +892,7 @@ int main(int argc, char *argv[]) mY.setEncodeDateAndTimeTypesAsBinary(A4); mY.setDatetimeFractionalSecondPrecision(A5); mY.setDisableUnselectedChoiceEncoding(A6); + mY.setPreserveSignOfNegativeZero(A7); if (veryVerbose) cout << "\ta. Check initial value of 'y'." << endl; if (veryVeryVerbose) { T_ T_ P(Y) } @@ -889,6 +903,7 @@ int main(int argc, char *argv[]) ASSERT(A4 == X.encodeDateAndTimeTypesAsBinary()); ASSERT(A5 == Y.datetimeFractionalSecondPrecision()); ASSERT(A6 == Y.disableUnselectedChoiceEncoding()); + ASSERT(A7 == Y.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'y' 'w', 'x', 'y'" << endl; @@ -913,6 +928,7 @@ int main(int argc, char *argv[]) ASSERT(A4 == Z.encodeDateAndTimeTypesAsBinary()); ASSERT(A5 == Z.datetimeFractionalSecondPrecision()); ASSERT(A6 == Z.disableUnselectedChoiceEncoding()); + ASSERT(A7 == Z.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'z' 'w', 'x', 'y', 'z'." << endl; @@ -933,6 +949,7 @@ int main(int argc, char *argv[]) mZ.setEncodeDateAndTimeTypesAsBinary(D4); mZ.setDatetimeFractionalSecondPrecision(D5); mZ.setDisableUnselectedChoiceEncoding(D6); + mZ.setPreserveSignOfNegativeZero(D7); if (veryVerbose) cout << "\ta. Check new value of 'z'." << endl; if (veryVeryVerbose) { T_ T_ P(Z) } @@ -943,6 +960,7 @@ int main(int argc, char *argv[]) ASSERT(D4 == Z.encodeDateAndTimeTypesAsBinary()); ASSERT(D5 == Z.datetimeFractionalSecondPrecision()); ASSERT(D6 == Z.disableUnselectedChoiceEncoding()); + ASSERT(D7 == Z.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'z' 'w', 'x', 'y', 'z'." << endl; @@ -967,6 +985,7 @@ int main(int argc, char *argv[]) ASSERT(A4 == W.encodeDateAndTimeTypesAsBinary()); ASSERT(A5 == W.datetimeFractionalSecondPrecision()); ASSERT(A6 == W.disableUnselectedChoiceEncoding()); + ASSERT(A7 == W.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'w' 'w', 'x', 'y', 'z'." << endl; @@ -991,6 +1010,7 @@ int main(int argc, char *argv[]) ASSERT(D4 == W.encodeDateAndTimeTypesAsBinary()); ASSERT(D5 == W.datetimeFractionalSecondPrecision()); ASSERT(D6 == W.disableUnselectedChoiceEncoding()); + ASSERT(D7 == W.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'x' 'w', 'x', 'y', 'z'." << endl; @@ -1015,6 +1035,7 @@ int main(int argc, char *argv[]) ASSERT(A4 == X.encodeDateAndTimeTypesAsBinary()); ASSERT(A5 == X.datetimeFractionalSecondPrecision()); ASSERT(A6 == X.disableUnselectedChoiceEncoding()); + ASSERT(A7 == X.preserveSignOfNegativeZero()); if (veryVerbose) cout << "\tb. Try equality operators: 'x' 'w', 'x', 'y', 'z'." << endl; diff --git a/groups/bal/balber/balber_beruniversaltagnumber.t.cpp b/groups/bal/balber/balber_beruniversaltagnumber.t.cpp index 9f99d393fe..aa45e898db 100644 --- a/groups/bal/balber/balber_beruniversaltagnumber.t.cpp +++ b/groups/bal/balber/balber_beruniversaltagnumber.t.cpp @@ -28,13 +28,13 @@ #include #include +#include #include -#include #include #include -#include +#include #include #include #include @@ -498,10 +498,10 @@ int bdlat_typeCategoryAccessArray( // 'bslmf::Nil' tag object otherwise. Return the result from the // invocation of 'accessor'. { - typedef - typename bslmf::If::VALUE, - bdlat_TypeCategory::Array, - bslmf::Nil>::Type Tag; + typedef typename + bsl::conditional::VALUE, + bdlat_TypeCategory::Array, + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -517,10 +517,10 @@ int bdlat_typeCategoryAccessChoice( // 'bslmf::Nil' tag object otherwise. Return the result from the // invocation of 'accessor'. { - typedef - typename bslmf::If::VALUE, - bdlat_TypeCategory::Choice, - bslmf::Nil>::Type Tag; + typedef typename + bsl::conditional::VALUE, + bdlat_TypeCategory::Choice, + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -536,10 +536,10 @@ int bdlat_typeCategoryAccessCustomizedType( // 'bdlat_customizedtypefunctions', or a 'bslmf::Nil' tag object otherwise. // Return the result from the invocation of 'accessor'. { - typedef typename bslmf::If< + typedef typename bsl::conditional< bdlat_CustomizedTypeFunctions::IsCustomizedType::VALUE, bdlat_TypeCategory::CustomizedType, - bslmf::Nil>::Type Tag; + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -555,10 +555,10 @@ int bdlat_typeCategoryAccessEnumeration( // or a 'bslmf::Nil' tag object otherwise. Return the result from the // invocation of 'accessor'. { - typedef typename bslmf::If< + typedef typename bsl::conditional< bdlat_EnumFunctions::IsEnumeration::VALUE, bdlat_TypeCategory::Enumeration, - bslmf::Nil>::Type Tag; + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -574,10 +574,10 @@ int bdlat_typeCategoryAccessNullableValue( // 'bdlat_nullablevaluefunctions', or a 'bslmf::Nil' tag object otherwise. // Return the result from the invocation of 'accessor'. { - typedef typename bslmf::If< + typedef typename bsl::conditional< bdlat_NullableValueFunctions::IsNullableValue::VALUE, bdlat_TypeCategory::NullableValue, - bslmf::Nil>::Type Tag; + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -593,10 +593,10 @@ int bdlat_typeCategoryAccessSequence( // 'bdlat_sequencefunctions', or a 'bslmf::Nil' tag object otherwise. // Return the result from the invocation of 'accessor'. { - typedef typename bslmf::If< + typedef typename bsl::conditional< bdlat_SequenceFunctions::IsSequence::VALUE, bdlat_TypeCategory::Sequence, - bslmf::Nil>::Type Tag; + bslmf::Nil>::type Tag; return accessor(object.value(), Tag()); } @@ -612,12 +612,12 @@ int bdlat_typeCategoryAccessSimple( // tag object otherwise. Return the result from the invocation of // 'accessor'. { - typedef typename bslmf::If< + typedef typename bsl::conditional< bdlat_TypeCategory::e_SIMPLE_CATEGORY == static_cast( bdlat_TypeCategory::Select::e_SELECTION), bdlat_TypeCategory::Simple, - bslmf::Nil>::Type Tag; + bslmf::Nil>::type Tag; // If the type category of 'VALUE_TYPE' is 'Simple', then 'Tag' is // 'bdlat_TypeCategory::Simple', and 'bslmf::Nil' otherwise. This // detection is done differently than for the complex type categories diff --git a/groups/bal/balber/balber_berutil.cpp b/groups/bal/balber/balber_berutil.cpp index 89c509292b..ad6f3cc14b 100644 --- a/groups/bal/balber/balber_berutil.cpp +++ b/groups/bal/balber/balber_berutil.cpp @@ -29,8 +29,6 @@ BSLS_IDENT_RCSID(balber_berutil_cpp, "$Id$ $CSID$") #include -#include - #include #include @@ -41,9 +39,11 @@ BSLS_IDENT_RCSID(balber_berutil_cpp, "$Id$ $CSID$") #include #include -namespace BloombergLP { -namespace balber { namespace { +namespace u { + +typedef BloombergLP::bsls::Types::Uint64 Uint64; +typedef BloombergLP::balber::BerDecoderOptions BerDecoderOptions; // ====================================== // struct BerUtil_64BitFloatingPointMasks @@ -58,10 +58,10 @@ struct BerUtil_64BitFloatingPointMasks { // too large to be used as enumerators on some platforms. // CLASS DATA - static const bsls::Types::Uint64 k_DOUBLE_EXPONENT_MASK; - static const bsls::Types::Uint64 k_DOUBLE_MANTISSA_MASK; - static const bsls::Types::Uint64 k_DOUBLE_MANTISSA_IMPLICIT_ONE_MASK; - static const bsls::Types::Uint64 k_DOUBLE_SIGN_MASK; + static const u::Uint64 k_DOUBLE_EXPONENT_MASK; + static const u::Uint64 k_DOUBLE_MANTISSA_MASK; + static const u::Uint64 k_DOUBLE_MANTISSA_IMPLICIT_ONE_MASK; + static const u::Uint64 k_DOUBLE_SIGN_MASK; }; // -------------------------------------- @@ -69,23 +69,39 @@ struct BerUtil_64BitFloatingPointMasks { // -------------------------------------- // CLASS DATA -const bsls::Types::Uint64 - BerUtil_64BitFloatingPointMasks::k_DOUBLE_EXPONENT_MASK = - 0x7FF0000000000000ULL; +const u::Uint64 BerUtil_64BitFloatingPointMasks::k_DOUBLE_EXPONENT_MASK = + 0x7FF0000000000000ULL; -const bsls::Types::Uint64 - BerUtil_64BitFloatingPointMasks::k_DOUBLE_MANTISSA_MASK = - 0x000FFFFFFFFFFFFFULL; +const u::Uint64 BerUtil_64BitFloatingPointMasks::k_DOUBLE_MANTISSA_MASK = + 0x000FFFFFFFFFFFFFULL; -const bsls::Types::Uint64 - BerUtil_64BitFloatingPointMasks::k_DOUBLE_MANTISSA_IMPLICIT_ONE_MASK = - 0x0010000000000000ULL; +const u::Uint64 BerUtil_64BitFloatingPointMasks:: + k_DOUBLE_MANTISSA_IMPLICIT_ONE_MASK = + 0x0010000000000000ULL; -const bsls::Types::Uint64 BerUtil_64BitFloatingPointMasks::k_DOUBLE_SIGN_MASK = - 0x8000000000000000ULL; +const u::Uint64 BerUtil_64BitFloatingPointMasks::k_DOUBLE_SIGN_MASK = + 0x8000000000000000ULL; +// FREE FUNCTIONS +void warnOnce() + // The first time this is called, issue an explanatory warning. +{ + BSLMT_ONCE_DO { + BSLS_LOG_WARN( + "[BDE_INTERNAL] The current process will decode an empty string" + " as the default value for an element in the type currently being" + " decoded. This behavior is erroneous and will eventually be" + " deprecated. The owners of the current process should be" + " contacted to audit it for dependence on this behavior."); + } +} + +} // close namespace u } // close unnamed namespace +namespace BloombergLP { +namespace balber { + // -------------- // struct BerUtil // -------------- @@ -325,7 +341,8 @@ int BerUtil_LengthImpUtil::getEndOfContentOctets( // Length Encoding Functions -int BerUtil_LengthImpUtil::putLength(bsl::streambuf *streamBuf, int length) +int BerUtil_LengthImpUtil::putLength(bsl::streambuf *streamBuf, + int length) { enum { SUCCESS = 0, FAILURE = -1 }; @@ -361,6 +378,7 @@ int BerUtil_LengthImpUtil::putLength(bsl::streambuf *streamBuf, int length) int BerUtil_LengthImpUtil::putIndefiniteLengthOctet(bsl::streambuf *streamBuf) { // Extra unsigned char cast needed to suppress warning on Windows. + int writtenOctet = streamBuf->sputc(static_cast( static_cast(k_INDEFINITE_LENGTH_OCTET))); @@ -674,7 +692,7 @@ void BerUtil_FloatingPointImpUtil::assembleDouble(double *value, long long mantissa, int sign) { - typedef BerUtil_64BitFloatingPointMasks WideBitMasks; + typedef u::BerUtil_64BitFloatingPointMasks WideBitMasks; unsigned long long longLongValue; @@ -694,7 +712,7 @@ void BerUtil_FloatingPointImpUtil::normalizeMantissaAndAdjustExp( int *exponent, bool denormalized) { - typedef BerUtil_64BitFloatingPointMasks WideBitMasks; + typedef u::BerUtil_64BitFloatingPointMasks WideBitMasks; if (!denormalized) { // If number is not denormalized then need to prefix with implicit one. @@ -717,7 +735,7 @@ void BerUtil_FloatingPointImpUtil::parseDouble(int *exponent, int *sign, double value) { - typedef BerUtil_64BitFloatingPointMasks WideBitMasks; + typedef u::BerUtil_64BitFloatingPointMasks WideBitMasks; unsigned long long longLongValue; bsl::memcpy(&longLongValue, &value, sizeof(unsigned long long)); @@ -746,19 +764,41 @@ int BerUtil_FloatingPointImpUtil::getDoubleValue(double *value, int nextOctet = streamBuf->sbumpc(); - if (k_POSITIVE_INFINITY_ID == nextOctet) { - assembleDouble( - value, k_DOUBLE_INFINITY_EXPONENT_ID, k_INFINITY_MANTISSA_ID, 0); - return SUCCESS; // RETURN - } - else if (k_NEGATIVE_INFINITY_ID == nextOctet) { - assembleDouble( - value, k_DOUBLE_INFINITY_EXPONENT_ID, k_INFINITY_MANTISSA_ID, 1); - return SUCCESS; // RETURN - } - else if (k_NAN_ID == nextOctet) { - assembleDouble(value, k_DOUBLE_INFINITY_EXPONENT_ID, 1, 0); - return SUCCESS; // RETURN + if (1 == length) { + switch (nextOctet) { + case k_POSITIVE_INFINITY_ID: { + assembleDouble(value, + k_DOUBLE_INFINITY_EXPONENT_ID, + k_INFINITY_MANTISSA_ID, + 0); + + return SUCCESS; // RETURN + } break; + case k_NEGATIVE_INFINITY_ID: { + assembleDouble(value, + k_DOUBLE_INFINITY_EXPONENT_ID, + k_INFINITY_MANTISSA_ID, + 1); + + return SUCCESS; // RETURN + } break; + case k_NAN_ID: { + assembleDouble(value, + k_DOUBLE_INFINITY_EXPONENT_ID, + 1, + 0); + + return SUCCESS; // RETURN + } break; + case k_NEGATIVE_ZERO_ID: { + *value = -0.0; + + return SUCCESS; // RETURN + } break; + default: { + ; // do nothing + } + } } if (!(nextOctet & static_cast(k_REAL_BINARY_ENCODING))) { @@ -828,7 +868,7 @@ int BerUtil_FloatingPointImpUtil::getDoubleValue(double *value, // Shift the mantissa left by shift amount, account for the implicit // one, and then removing it. - typedef BerUtil_64BitFloatingPointMasks WideBitMasks; + typedef u::BerUtil_64BitFloatingPointMasks WideBitMasks; mantissa <<= shift + 1; mantissa &= ~WideBitMasks::k_DOUBLE_MANTISSA_IMPLICIT_ONE_MASK; } @@ -892,15 +932,24 @@ int BerUtil_FloatingPointImpUtil::putDecimal64Value( return 0; } -int BerUtil_FloatingPointImpUtil::putDoubleValue(bsl::streambuf *streamBuf, - double value) +int BerUtil_FloatingPointImpUtil::putDoubleValue( + bsl::streambuf *streamBuf, + double value, + const BerEncoderOptions *options) { enum { SUCCESS = 0, FAILURE = -1 }; // If 0 == value, put out length = 0 and return. if (0.0 == value) { - return 0 == streamBuf->sputc(0) ? SUCCESS : FAILURE; // RETURN + static const char negativeZeroSeq[] = { k_NEGATIVE_ZERO_LEN, + k_NEGATIVE_ZERO_ID }; + + bool ok = (options && options->preserveSignOfNegativeZero()) && + bdlb::Float::signBit(value) + ? 2 == streamBuf->sputn(negativeZeroSeq, 2) + : k_POSITIVE_ZERO_LEN == streamBuf->sputc(k_POSITIVE_ZERO_LEN); + return ok ? SUCCESS : FAILURE; // RETURN } // Else parse double value. @@ -1011,7 +1060,7 @@ int BerUtil_StringImpUtil::putChars(bsl::streambuf *streamBuf, return 0; } -// 'bsl::string' Decoding +// '*::string' Decoding int BerUtil_StringImpUtil::getStringValue(bsl::string *value, bsl::streambuf *streamBuf, @@ -1020,16 +1069,7 @@ int BerUtil_StringImpUtil::getStringValue(bsl::string *value, { if (0 == length) { if (options.defaultEmptyStrings() && !value->empty()) { - BSLMT_ONCE_DO - { - BSLS_LOG_WARN("[BDE_INTERNAL] The current process will decode " - "an empty string as the default value for an " - "element in the type currently being decoded. " - "This behavior is erroneous and will eventually " - "be deprecated. The owners of the current " - "process should be contacted to audit it for " - "dependence on this behavior."); - } + u::warnOnce(); } if (!options.defaultEmptyStrings()) { @@ -1045,7 +1085,7 @@ int BerUtil_StringImpUtil::getStringValue(bsl::string *value, value->resize(length); const bsl::streamsize bytesConsumed = - streamBuf->sgetn(&(*value)[0], length); + streamBuf->sgetn(&(*value)[0], length); if (length != bytesConsumed) { return -1; // RETURN } @@ -1333,6 +1373,7 @@ int BerUtil_DateImpUtil::putCompactBinaryDateValue( const BerEncoderOptions *options) { BSLS_ASSERT_SAFE(0 == value.offset()); + return putCompactBinaryDateValue(streamBuf, value.localDate(), options); } @@ -1370,7 +1411,9 @@ int BerUtil_DateImpUtil::putCompactBinaryDateTzValue( } return IntegerUtil::putIntegerGivenLength( - streamBuf, daysSinceEpoch, length - TimezoneUtil::k_TIMEZONE_LENGTH); + streamBuf, + daysSinceEpoch, + length - TimezoneUtil::k_TIMEZONE_LENGTH); // RETURN } @@ -1443,10 +1486,10 @@ int BerUtil_DateImpUtil::putDateValue(bsl::streambuf *streamBuf, switch (selectDateEncoding(value, options)) { case DateEncoding::e_ISO8601_DATE: { - return putIso8601DateValue(streamBuf, value, options); + return putIso8601DateValue(streamBuf, value, options); // RETURN } break; case DateEncoding::e_COMPACT_BINARY_DATE: { - return putCompactBinaryDateValue(streamBuf, value, options); + return putCompactBinaryDateValue(streamBuf, value, options); // RETURN } break; } @@ -1454,7 +1497,7 @@ int BerUtil_DateImpUtil::putDateValue(bsl::streambuf *streamBuf, #if BSLA_UNREACHABLE_IS_ACTIVE BSLA_UNREACHABLE; #else - return -1; // RETURN + return -1; #endif } @@ -1753,6 +1796,7 @@ int BerUtil_TimeImpUtil::putCompactBinaryTimeTzValue( return IntegerUtil::putIntegerGivenLength( streamBuf, serialTime, length - TimezoneUtil::k_TIMEZONE_LENGTH); + // RETURN } if (0 != LengthUtil::putLength(streamBuf, length)) { @@ -1837,7 +1881,7 @@ int BerUtil_DatetimeImpUtil::millisecondsSinceEpochToDatetime( static_cast(millisecondsSinceEpoch - daysSinceEpoch * Ratio::k_MILLISECONDS_PER_DAY); - int hour = millisecondsSinceMidnight / Ratio::k_MILLISECONDS_PER_HOUR; + int hour = millisecondsSinceMidnight / Ratio::k_MILLISECONDS_PER_HOUR_32; const int minute = (millisecondsSinceMidnight - hour * Ratio::k_MILLISECONDS_PER_HOUR_32) / diff --git a/groups/bal/balber/balber_berutil.h b/groups/bal/balber/balber_berutil.h index 079765ae64..25055d0081 100644 --- a/groups/bal/balber/balber_berutil.h +++ b/groups/bal/balber/balber_berutil.h @@ -120,6 +120,7 @@ BSLS_IDENT("$Id: $") #include #include +#include #include #include @@ -831,9 +832,13 @@ struct BerUtil_FloatingPointImpUtil { k_DOUBLE_NUM_MANTISSA_BYTES = 7, k_DOUBLE_BIAS = 1023, + k_POSITIVE_ZERO_LEN = 0, + k_NEGATIVE_ZERO_LEN = 1, + k_POSITIVE_INFINITY_ID = 0x40, k_NEGATIVE_INFINITY_ID = 0x41, k_NAN_ID = 0x42, + k_NEGATIVE_ZERO_ID = 0x43, k_DOUBLE_INFINITY_EXPONENT_ID = 0x7FF, k_INFINITY_MANTISSA_ID = 0, @@ -935,23 +940,29 @@ struct BerUtil_FloatingPointImpUtil { // Encoding - static int putFloatValue(bsl::streambuf *streamBuf, float value); + static int putFloatValue(bsl::streambuf *streamBuf, + float value, + const BerEncoderOptions *options = 0); // Write the length and contents octets of the BER encoding of the // specified real 'value' (as defined in the specification) to the - // output sequence of the specified 'streamBuf'. Return 0 if - // successful, and a non-zero value otherwise. The operation succeeds - // if all bytes corresponding to the length and contents octets are - // written to the 'streamBuf' without the write position becoming - // unavailable. + // output sequence of the specified 'streamBuf'. Optionally specify + // 'options', which will indicate whether '-0.0f' will be preserved or + // encoded as '+0.0f'. Return 0 if successful, and a non-zero value + // otherwise. The operation succeeds if all bytes corresponding to the + // length and contents octets are written to the 'streamBuf' without + // the write position becoming unavailable. - static int putDoubleValue(bsl::streambuf *streamBuf, double value); + static int putDoubleValue(bsl::streambuf *streamBuf, + double value, + const BerEncoderOptions *options = 0); // Write the length and contents octets of the BER encoding of the // specified real 'value' (as defined in the specification) to the - // output sequence of the specified 'streamBuf'. Return 0 if - // successful, and a non-zero value otherwise. The operation succeeds - // if all bytes corresponding to the length and contents octets are - // written to the 'streamBuf' without the write position becoming - // unavailable. + // output sequence of the specified 'streamBuf'. Optionally specify + // 'options', which will indicate whether '-0.0' will be preserved or + // encoded as '+0.0'. Return 0 if successful, and a non-zero value + // otherwise. The operation succeeds if all bytes corresponding to the + // length and contents octets are written to the 'streamBuf' without + // the write position becoming unavailable. static int putDecimal64Value(bsl::streambuf *streamBuf, bdldfp::Decimal64 value); @@ -4020,7 +4031,7 @@ int BerUtil_BooleanImpUtil::putBoolValue(bsl::streambuf *streamBuf, bool value) if (static_cast(value) != streamBuf->sputc(static_cast(value ? 1 : 0))) { - return -1; + return -1; // RETURN } return 0; @@ -4069,14 +4080,18 @@ int BerUtil_IntegerImpUtil::getNumOctetsToStream(TYPE value) static const TYPE POS_MASK = TYPE(~NEG_MASK); while ((value & POS_MASK) == value) { value = static_cast(value << 8); + // shift out redundant high-order 0x00 + --numBytes; } } else { // 0 > value while ((value | NEG_MASK) == value) { value = static_cast(value << 8); + // shift out redundant high-order 0xFF + --numBytes; } } @@ -4238,18 +4253,21 @@ int BerUtil_CharacterImpUtil::getCharValue(char *value, int length) { switch (length) { - case 1: - break; - case 2: + case 1: { + ; // do nothing + } break; + case 2: { if (0 != streamBuf->sbumpc()) { // see 'getIntegerValue', if this 'char' had been encoded as // 'unsigned' there might be a leading 0 which is acceptable, but // any other value for the first byte is invalid + return -1; // RETURN } - break; - default: + } break; + default: { return -1; // RETURN + } } int valueOctet = streamBuf->sbumpc(); @@ -4347,10 +4365,14 @@ int BerUtil_FloatingPointImpUtil::getFloatValue(float *value, // Encoding inline -int BerUtil_FloatingPointImpUtil::putFloatValue(bsl::streambuf *streamBuf, - float value) +int BerUtil_FloatingPointImpUtil::putFloatValue( + bsl::streambuf *streamBuf, + float value, + const BerEncoderOptions *options) { - return putDoubleValue(streamBuf, static_cast(value)); + return putDoubleValue(streamBuf, + static_cast(value), + options); } // ---------------------------- @@ -4461,6 +4483,7 @@ bool BerUtil_ExtendedBinaryEncodingUtil::useExtendedBinaryEncoding( if (!options) { // If encoding options are not specified, by default the ISO8601 format // is used. + return false; // RETURN } @@ -4780,7 +4803,7 @@ int BerUtil_DateAndTimeHeaderImpUtil::getValue(Header *value, } value->makeExtendedBinaryWithTimezone(timezoneOffset); - return 0; + return 0; // RETURN } break; } @@ -5127,7 +5150,7 @@ BerUtil_TimeEncoding::Value BerUtil_TimeImpUtil::selectTimeEncoding( return TimeEncoding::e_COMPACT_BINARY_TIME; // RETURN } - return TimeEncoding::e_ISO8601_TIME; // RETURN + return TimeEncoding::e_ISO8601_TIME; } inline @@ -5199,11 +5222,11 @@ BerUtil_TimeTzEncoding::Value BerUtil_TimeImpUtil::selectTimeTzEncoding( if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options) && (0 == value.offset())) { - return TimeTzEncoding::e_COMPACT_BINARY_TIME; + return TimeTzEncoding::e_COMPACT_BINARY_TIME; // RETURN } if (ExtendedBinaryEncodingUtil::useBinaryEncoding(options)) { - return TimeTzEncoding::e_COMPACT_BINARY_TIMETZ; + return TimeTzEncoding::e_COMPACT_BINARY_TIMETZ; // RETURN } return TimeTzEncoding::e_ISO8601_TIMETZ; @@ -5803,7 +5826,7 @@ int BerUtil_DatetimeImpUtil::detectDatetimeOrDatetimeTzEncoding( if (DateAndTimeHeaderUtil::isExtendedBinaryWithoutTimezone(firstByte)) { *encoding = DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIME; - return 0; // RETURN + return 0; // RETURN } if (DateAndTimeHeaderUtil::isExtendedBinaryWithTimezone(firstByte)) { @@ -6105,22 +6128,26 @@ int BerUtil_DatetimeImpUtil::getDatetimeOrDatetimeTzValue( switch (encoding) { case DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIME: { - return getIso8601DatetimeValue(value, streamBuf, length); + return getIso8601DatetimeValue(value, streamBuf, length); // RETURN } break; case DatetimeOrDatetimeTzEncoding::e_ISO8601_DATETIMETZ: { - return getIso8601DatetimeTzValue(value, streamBuf, length); + return getIso8601DatetimeTzValue(value, streamBuf, length); // RETURN } break; case DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIME: { return getCompactBinaryDatetimeValue(value, streamBuf, length); + // RETURN } break; case DatetimeOrDatetimeTzEncoding::e_COMPACT_BINARY_DATETIMETZ: { return getCompactBinaryDatetimeTzValue(value, streamBuf, length); + // RETURN } break; case DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIME: { return getExtendedBinaryDatetimeValue(value, streamBuf, length); + // RETURN } break; case DatetimeOrDatetimeTzEncoding::e_EXTENDED_BINARY_DATETIMETZ: { return getExtendedBinaryDatetimeTzValue(value, streamBuf, length); + // RETURN } break; } @@ -6348,17 +6375,21 @@ int BerUtil_PutValueImpUtil::putValue(bsl::streambuf *streamBuf, inline int BerUtil_PutValueImpUtil::putValue(bsl::streambuf *streamBuf, float value, - const BerEncoderOptions *) + const BerEncoderOptions *options) { - return FloatingPointUtil::putFloatValue(streamBuf, value); + return FloatingPointUtil::putFloatValue(streamBuf, + value, + options); } inline int BerUtil_PutValueImpUtil::putValue(bsl::streambuf *streamBuf, double value, - const BerEncoderOptions *) + const BerEncoderOptions *options) { - return FloatingPointUtil::putDoubleValue(streamBuf, value); + return FloatingPointUtil::putDoubleValue(streamBuf, + value, + options); } inline diff --git a/groups/bal/balber/balber_berutil.t.cpp b/groups/bal/balber/balber_berutil.t.cpp index 6c9ebe7f0c..dcb4f57548 100644 --- a/groups/bal/balber/balber_berutil.t.cpp +++ b/groups/bal/balber/balber_berutil.t.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -138,7 +139,8 @@ using namespace bsl; // [27] CONCERN: 'getValue' reports all failures to read from stream buffer // [28] CONCERN: 'put'- & 'getValue' for date/time types in extended binary fmt // [29] CONCERN: 'putValue' encoding formation selection - +// [30] CONCERN: TESTING +/- ZERO FLOATING-POINT\n" +// [31] USAGE EXAMPLE // ============================================================================ // STANDARD BDE ASSERT TEST MACRO // ---------------------------------------------------------------------------- @@ -619,7 +621,7 @@ namespace RandomValueFunctions { // Deterministically load a pseudo-random value into the specified // 'value' using the specified 'loader'. -} // close RandomValueFunctions namespace +} // close namespace RandomValueFunctions // ====================== // struct RandomValueUtil @@ -769,9 +771,10 @@ class Md5Fingerprint { // to this object. unsigned char& operator[](bsl::size_t index); - // Return a reference providing modifiable access to the 'unsigned char' - // element of the 'array' attribute of this object at the specified - // 'index'. The behavior is undefined unless '16 > index'. + // Return a reference providing modifiable access to the + // 'unsigned char' element of the 'array' attribute of this object at + // the specified 'index'. The behavior is undefined unless + // '16 > index'. void setTheUintAt(bsl::size_t index, unsigned int value); // Assign to the 4 'unsigned char' elements of the 'array' attribute of @@ -1379,9 +1382,9 @@ struct Md5StateUtil_ImplUtil { unsigned int s); // Load into the specified 'a', 'b', 'c', and 'd' a value // deterministically determined from the specified 'x', 'i', 'k', and - // 's'. The essential behavior of this function is equivalent to the - // second round operation for updating the MD5 state as specified in the - // April 1992 revision of IETF RFC 1321. + // 's'. The essential behavior of this function is equivalent to the + // second round operation for updating the MD5 state as specified in + // the April 1992 revision of IETF RFC 1321. static void round3Op(unsigned int *a, unsigned int *b, @@ -1442,6 +1445,7 @@ class Md5ChecksumAlgorithm { // DATA Md5State d_state; // current MD5 state + private: // NOT IMPLEMENTED Md5ChecksumAlgorithm(const Md5ChecksumAlgorithm&) BSLS_KEYWORD_DELETED; Md5ChecksumAlgorithm& operator=(const Md5ChecksumAlgorithm) @@ -1561,6 +1565,11 @@ class PutValueFingerprint { // 'balber::BerEncoderOptions' attribute to supply to // 'balber::BerUtil::putValue' + bool d_preserveSignOfNegativeZero; + // value to fix for the 'encodeDateAndTimeTypesAsBinary' + // 'balber::BerEncoderOptions' attribute to supply to + // 'balber::BerUtil::putValue' + public: // CREATORS PutValueFingerprint(); @@ -1585,6 +1594,10 @@ class PutValueFingerprint { // Assign the specified 'value' to the 'encodeDateAndTimeTypesAsBinary' // attribute of this object. + void setPreserveSignOfNegativeZero(bool value); + // Assign the specified 'value' to the 'preserveSignOfNegativeZero' + // attribute of this object. + // ACCESSORS int seed() const; // Return the value of the 'seed' attribute of this object. @@ -1599,6 +1612,10 @@ class PutValueFingerprint { bool encodeDateAndTimeTypesAsBinary() const; // Return the value of the 'encodeDateAndTimeTypesAsBinary' attribute // of this object. + + bool preserveSignOfNegativeZero() const; + // Return the value of the 'preserveSignOfNegativeZero' attribute of + // this object. }; // FREE FUNCTIONS @@ -1667,6 +1684,11 @@ class GetValueFingerprint { // 'balber::BerEncoderOptions' attribute to supply to // 'balber::BerUtil::putValue' + bool d_preserveSignOfNegativeZero; + // value to fix for the 'encodeDateAndTimeTypesAsBinary' + // 'balber::BerEncoderOptions' attribute to supply to + // 'balber::BerUtil::putValue' + public: // CREATORS GetValueFingerprint(); @@ -1691,6 +1713,10 @@ class GetValueFingerprint { // Assign the specified 'value' to the 'encodeDateAndTimeTypesAsBinary' // attribute of this object. + void setPreserveSignOfNegativeZero(bool value); + // Assign the specified 'value' to the 'preserveSignOfNegativeZero' + // attribute of this object. + // ACCESSORS int seed() const; // Return the value of the 'seed' attribute of this object. @@ -1705,6 +1731,10 @@ class GetValueFingerprint { bool encodeDateAndTimeTypesAsBinary() const; // Return the value of the 'encodeDateAndTimeTypesAsBinary' attribute // of this object. + + bool preserveSignOfNegativeZero() const; + // Return the value of the 'preserveSignOfNegativeZero' attribute of + // this object. }; // FREE FUNCTIONS @@ -1970,12 +2000,12 @@ void Case27Tester::operator()(int LINE1, const SIMPLE_TYPE& VALUE) const if (0 == rc) { LOOP2_ASSERT_EQ(LINE1, LINE2, VALUE, inValue); + if (VALUE != inValue) continue; - LOOP2_ASSERT_EQ(LINE1, - LINE2, - static_cast(accumNumBytesConsumed), - inStreamBufLength); + ASSERTV(LINE1, LINE2, + static_cast(accumNumBytesConsumed) == + inStreamBufLength); if (static_cast(accumNumBytesConsumed) != inStreamBufLength) { continue; @@ -1986,7 +2016,8 @@ void Case27Tester::operator()(int LINE1, const SIMPLE_TYPE& VALUE) const } } while (0 != rc); - LOOP2_ASSERT_EQ(LINE1, LINE2, inStreamBufLength, outStreamBuf.length()); + LOOP2_ASSERT_EQ(LINE1, LINE2, inStreamBufLength, + outStreamBuf.length()); } } @@ -2028,14 +2059,12 @@ int ByteBufferUtil::loadBuffer(int *numBytesWritten, unsigned char currentNibble; if ('A' <= character && character <= 'Z') { - currentNibble = static_cast('\x0A') + - (static_cast(character) - - static_cast('A')); + currentNibble = static_cast( + '\x0A' + character - 'A'); } else if ('0' <= character && character <= '9') { - currentNibble = static_cast('\x00') + - (static_cast(character) - - static_cast('0')); + currentNibble = static_cast( + '\x00' + character - '0'); } else { return -1; // RETURN @@ -2048,7 +2077,7 @@ int ByteBufferUtil::loadBuffer(int *numBytesWritten, } break; case e_NEEDS_LOW_NIBBLE: { const char byte = - static_cast(stateHighNibble | currentNibble); + static_cast(stateHighNibble | currentNibble); *buffer++ = byte; ++*numBytesWritten; @@ -2088,13 +2117,16 @@ unsigned char RandomInputIterator::generateValue(int *seed) *seed = unsignedSeed; - unsigned char hiByte = (unsignedSeed >> 24) & 0x7F; - unsigned char loByte = (unsignedSeed >> 16) & 0xFF; + unsigned char hiByte = static_cast( + (unsignedSeed >> 24) & 0x7F); + unsigned char loByte = static_cast( + (unsignedSeed >> 16) & 0xFF); return hiByte ^ loByte; } // CLASS METHODS +BSLA_MAYBE_UNUSED bool RandomInputIterator::areEqual(const RandomInputIterator& lhs, const RandomInputIterator& rhs) { @@ -2102,10 +2134,12 @@ bool RandomInputIterator::areEqual(const RandomInputIterator& lhs, : true); BSLS_ASSERT(lhs.d_value != rhs.d_value ? lhs.d_seed != rhs.d_seed : true); + return lhs.d_seed == rhs.d_seed; } // CREATORS +BSLA_MAYBE_UNUSED RandomInputIterator::RandomInputIterator() : d_seed(0) , d_value(generateValue(&d_seed)) @@ -2125,6 +2159,7 @@ RandomInputIterator::RandomInputIterator(const RandomInputIterator& original) } // MANIPULATORS +BSLA_MAYBE_UNUSED RandomInputIterator& RandomInputIterator::operator=( const RandomInputIterator& original) { @@ -2156,6 +2191,7 @@ const unsigned char& RandomInputIterator::operator*() const return d_value; } +BSLA_MAYBE_UNUSED const unsigned char *RandomInputIterator::operator->() const { return &d_value; @@ -2475,7 +2511,7 @@ void loadRandomValue(VALUE_TYPE *value, LOADER& loader) return loader(value); } -} // close RandomValueFunctions namespace +} // close namespace RandomValueFunctions // ---------------------- // struct RandomValueUtil @@ -2502,6 +2538,7 @@ INTEGRAL_TYPE RandomValueUtil::generateModulo(LOADER& loader, INTEGRAL_TYPE base) { BSLS_ASSERT(base >= 0); + return generate(loader) % base; } @@ -2695,7 +2732,7 @@ void loadRandomValue(bdlt::Time *value, LOADER& loader) const unsigned microsecond = millisecondRemainder; BSLS_ASSERT( - bdlt::Time::isValid(hour, minute, second, millisecond, microsecond)); + bdlt::Time::isValid(hour, minute, second, millisecond, microsecond)); value->setTime(hour, minute, second, millisecond, microsecond); } @@ -2756,6 +2793,7 @@ void ByteArrayUtil::setTheUintAt(unsigned char *begin, BSLS_ASSERT(begin <= end); BSLS_ASSERT(static_cast((end - begin) / sizeof(unsigned int)) > index); + static_cast(end); begin += index * sizeof(unsigned int); @@ -2774,6 +2812,7 @@ void ByteArrayUtil::setTheUint64At(unsigned char *begin, BSLS_ASSERT(begin <= end); BSLS_ASSERT(static_cast((end - begin) / sizeof(bsls::Types::Uint64)) > index); + static_cast(end); begin += index * sizeof(unsigned int); @@ -2795,6 +2834,7 @@ unsigned int ByteArrayUtil::theUintAt(const unsigned char *begin, BSLS_ASSERT(begin <= end); BSLS_ASSERT(static_cast((end - begin) / sizeof(unsigned int)) > index); + static_cast(end); begin += index * sizeof(unsigned int); @@ -2836,6 +2876,7 @@ Md5Fingerprint& Md5Fingerprint::operator=(const Md5Fingerprint& original) return *this; } +BSLA_MAYBE_UNUSED unsigned char& Md5Fingerprint::operator[](bsl::size_t index) { BSLS_ASSERT(k_SIZE > index); @@ -2848,22 +2889,26 @@ void Md5Fingerprint::setTheUintAt(bsl::size_t index, unsigned int value) ByteArrayUtil::setTheUintAt(d_value, d_value + k_SIZE, index, value); } +BSLA_MAYBE_UNUSED unsigned char *Md5Fingerprint::data() { return d_value; } +BSLA_MAYBE_UNUSED unsigned char *Md5Fingerprint::begin() { return d_value; } +BSLA_MAYBE_UNUSED unsigned char *Md5Fingerprint::end() { return d_value + k_SIZE; } // ACCESSORS +BSLA_MAYBE_UNUSED const unsigned char& Md5Fingerprint::operator[](bsl::size_t index) const { BSLS_ASSERT(k_SIZE > index); @@ -2876,16 +2921,19 @@ unsigned int Md5Fingerprint::theUintAt(bsl::size_t index) const return ByteArrayUtil::theUintAt(d_value, d_value + k_SIZE, index); } +BSLA_MAYBE_UNUSED bsl::size_t Md5Fingerprint::size() const { return k_SIZE; } +BSLA_MAYBE_UNUSED const unsigned char *Md5Fingerprint::begin() const { return d_value; } +BSLA_MAYBE_UNUSED const unsigned char *Md5Fingerprint::end() const { return d_value + k_SIZE; @@ -2919,8 +2967,10 @@ bsl::ostream& Md5Fingerprint::print(bsl::ostream& stream, for (const unsigned char *it = d_value; it != d_value + k_SIZE; ++it) { const unsigned char value = *it; - const unsigned char hiNibble = (value >> 4) & 0x0F; - const unsigned char loNibble = (value >> 0) & 0x0F; + const unsigned char hiNibble = static_cast( + (value >> 4) & 0x0F); + const unsigned char loNibble = static_cast( + (value >> 0) & 0x0F); const char hiNibbleChar = nibbleCharacters[hiNibble]; const char loNibbleChar = nibbleCharacters[loNibble]; @@ -3010,6 +3060,7 @@ Md5Block& Md5Block::operator=(const Md5Block& original) return *this; } +BSLA_MAYBE_UNUSED unsigned char &Md5Block::operator[](bsl::size_t index) { BSLS_ASSERT(d_numBytes > index); @@ -3017,6 +3068,7 @@ unsigned char &Md5Block::operator[](bsl::size_t index) return d_bytes[index]; } +BSLA_MAYBE_UNUSED void Md5Block::setTheUintAt(bsl::size_t index, unsigned int value) { ByteArrayUtil::setTheUintAt(d_bytes, d_bytes + d_numBytes, index, value); @@ -3053,6 +3105,7 @@ void Md5Block::resize(bsl::size_t newSize) } // ACCESSORS +BSLA_MAYBE_UNUSED const unsigned char& Md5Block::operator[](bsl::size_t index) const { BSLS_ASSERT(d_numBytes > index); @@ -3173,6 +3226,7 @@ bool Md5BlockInputIterator::areEqual(const Md5BlockInputIterator& lhs, } // CREATORS +BSLA_MAYBE_UNUSED Md5BlockInputIterator::Md5BlockInputIterator() : d_block() , d_iterator_p(0) @@ -3207,6 +3261,7 @@ Md5BlockInputIterator::Md5BlockInputIterator( } // MANIPULATORS +BSLA_MAYBE_UNUSED Md5BlockInputIterator& Md5BlockInputIterator::operator=( const Md5BlockInputIterator& original) { @@ -3238,6 +3293,7 @@ Md5BlockInputIterator& Md5BlockInputIterator::operator++() return *this; } +BSLA_MAYBE_UNUSED Md5BlockInputIterator Md5BlockInputIterator::operator++(int) { Md5BlockInputIterator result = *this; @@ -3251,6 +3307,7 @@ const Md5Block& Md5BlockInputIterator::operator*() const return d_block; } +BSLA_MAYBE_UNUSED const Md5Block *Md5BlockInputIterator::operator->() const { return &d_block; @@ -3306,6 +3363,7 @@ Md5State::Md5State() { } +BSLA_MAYBE_UNUSED Md5State::Md5State(const Md5Fingerprint& fingerprint) : d_fingerprint(fingerprint) , d_block() @@ -3313,6 +3371,7 @@ Md5State::Md5State(const Md5Fingerprint& fingerprint) { } +BSLA_MAYBE_UNUSED Md5State::Md5State(const Md5Fingerprint& fingerprint, const Md5Block& block) : d_fingerprint(fingerprint) , d_block(block) @@ -3320,6 +3379,7 @@ Md5State::Md5State(const Md5Fingerprint& fingerprint, const Md5Block& block) { } +BSLA_MAYBE_UNUSED Md5State::Md5State(const Md5Fingerprint& fingerprint, const Md5Block& block, bsls::Types::Uint64 numBlocksConsumed) @@ -3337,6 +3397,7 @@ Md5State::Md5State(const Md5State& original) } // MANIPULATORS +BSLA_MAYBE_UNUSED Md5State& Md5State::operator=(const Md5State& original) { d_fingerprint = original.d_fingerprint; @@ -3582,6 +3643,7 @@ void Md5StateUtil::digest(Md5Fingerprint *fingerprint, const Md5Block& block) fingerprint->setTheUintAt(3, d + dd); } +BSLA_MAYBE_UNUSED Md5Fingerprint Md5StateUtil::digest(const Md5State& state) { return digest(state.fingerprint(), state.block()); @@ -4167,6 +4229,11 @@ void PutValueFingerprint::setEncodeDateAndTimeTypesAsBinary(bool value) d_encodeDateAndTimeTypesAsBinary = value; } +void PutValueFingerprint::setPreserveSignOfNegativeZero(bool value) +{ + d_preserveSignOfNegativeZero = value; +} + // ACCESSORS int PutValueFingerprint::seed() const { @@ -4188,6 +4255,11 @@ bool PutValueFingerprint::encodeDateAndTimeTypesAsBinary() const return d_encodeDateAndTimeTypesAsBinary; } +bool PutValueFingerprint::preserveSignOfNegativeZero() const +{ + return d_preserveSignOfNegativeZero; +} + // FREE FUNCTIONS template void checksumAppend(CHECKSUM_ALGORITHM& checksumAlg, @@ -4198,6 +4270,8 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksumAlg, object.fractionalSecondPrecision()); encoderOptions.setEncodeDateAndTimeTypesAsBinary( object.encodeDateAndTimeTypesAsBinary()); + encoderOptions.setPreserveSignOfNegativeZero( + object.preserveSignOfNegativeZero()); enum SupportedTypes { e_BOOL, @@ -4367,6 +4441,11 @@ void GetValueFingerprint::setEncodeDateAndTimeTypesAsBinary(bool value) d_encodeDateAndTimeTypesAsBinary = value; } +void GetValueFingerprint::setPreserveSignOfNegativeZero(bool value) +{ + d_preserveSignOfNegativeZero = value; +} + // ACCESSORS int GetValueFingerprint::seed() const { @@ -4388,6 +4467,11 @@ bool GetValueFingerprint::encodeDateAndTimeTypesAsBinary() const return d_encodeDateAndTimeTypesAsBinary; } +bool GetValueFingerprint::preserveSignOfNegativeZero() const +{ + return d_preserveSignOfNegativeZero; +} + // FREE FUNCTIONS template void checksumAppend(HASHALG& hashAlg, const GetValueFingerprint& object) @@ -4397,6 +4481,8 @@ void checksumAppend(HASHALG& hashAlg, const GetValueFingerprint& object) object.fractionalSecondPrecision()); encoderOptions.setEncodeDateAndTimeTypesAsBinary( object.encodeDateAndTimeTypesAsBinary()); + encoderOptions.setPreserveSignOfNegativeZero( + object.preserveSignOfNegativeZero()); enum SupportedTypes { e_BOOL, @@ -4880,10 +4966,14 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksum, int value) { BSLMF_ASSERT(4 == sizeof(value)); - const signed char byte0 = (value >> (0 * 8)) & 0xFF; - const signed char byte1 = (value >> (1 * 8)) & 0xFF; - const signed char byte2 = (value >> (2 * 8)) & 0xFF; - const signed char byte3 = (value >> (3 * 8)) & 0xFF; + const signed char byte0 = static_cast( + (value >> (0 * 8)) & 0xFF); + const signed char byte1 = static_cast( + (value >> (1 * 8)) & 0xFF); + const signed char byte2 = static_cast( + (value >> (2 * 8)) & 0xFF); + const signed char byte3 = static_cast( + (value >> (3 * 8)) & 0xFF); checksumAppend(checksum, byte0); checksumAppend(checksum, byte1); @@ -4896,14 +4986,22 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksum, bsls::Types::Int64 value) { BSLMF_ASSERT(8 == sizeof(bsls::Types::Int64)); - const signed char byte0 = (value >> (0 * 8)) & 0xFF; - const signed char byte1 = (value >> (1 * 8)) & 0xFF; - const signed char byte2 = (value >> (2 * 8)) & 0xFF; - const signed char byte3 = (value >> (3 * 8)) & 0xFF; - const signed char byte4 = (value >> (4 * 8)) & 0xFF; - const signed char byte5 = (value >> (5 * 8)) & 0xFF; - const signed char byte6 = (value >> (6 * 8)) & 0xFF; - const signed char byte7 = (value >> (7 * 8)) & 0xFF; + const signed char byte0 = static_cast( + (value >> (0 * 8)) & 0xFF); + const signed char byte1 = static_cast( + (value >> (1 * 8)) & 0xFF); + const signed char byte2 = static_cast( + (value >> (2 * 8)) & 0xFF); + const signed char byte3 = static_cast( + (value >> (3 * 8)) & 0xFF); + const signed char byte4 = static_cast( + (value >> (4 * 8)) & 0xFF); + const signed char byte5 = static_cast( + (value >> (5 * 8)) & 0xFF); + const signed char byte6 = static_cast( + (value >> (6 * 8)) & 0xFF); + const signed char byte7 = static_cast( + (value >> (7 * 8)) & 0xFF); checksumAppend(checksum, byte0); checksumAppend(checksum, byte1); @@ -4920,10 +5018,14 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksum, unsigned int value) { BSLMF_ASSERT(4 == sizeof(unsigned int)); - const unsigned char byte0 = (value >> (0 * 8)) & 0xFF; - const unsigned char byte1 = (value >> (1 * 8)) & 0xFF; - const unsigned char byte2 = (value >> (2 * 8)) & 0xFF; - const unsigned char byte3 = (value >> (3 * 8)) & 0xFF; + const unsigned char byte0 = static_cast( + (value >> (0 * 8)) & 0xFF); + const unsigned char byte1 = static_cast( + (value >> (1 * 8)) & 0xFF); + const unsigned char byte2 = static_cast( + (value >> (2 * 8)) & 0xFF); + const unsigned char byte3 = static_cast( + (value >> (3 * 8)) & 0xFF); checksumAppend(checksum, byte0); checksumAppend(checksum, byte1); @@ -4936,14 +5038,22 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksum, bsls::Types::Uint64 value) { BSLMF_ASSERT(8 == sizeof(bsls::Types::Uint64)); - const unsigned char byte0 = (value >> (0 * 8)) & 0xFF; - const unsigned char byte1 = (value >> (1 * 8)) & 0xFF; - const unsigned char byte2 = (value >> (2 * 8)) & 0xFF; - const unsigned char byte3 = (value >> (3 * 8)) & 0xFF; - const unsigned char byte4 = (value >> (4 * 8)) & 0xFF; - const unsigned char byte5 = (value >> (5 * 8)) & 0xFF; - const unsigned char byte6 = (value >> (6 * 8)) & 0xFF; - const unsigned char byte7 = (value >> (7 * 8)) & 0xFF; + const unsigned char byte0 = + static_cast((value >> (0 * 8)) & 0xFF); + const unsigned char byte1 = + static_cast((value >> (1 * 8)) & 0xFF); + const unsigned char byte2 = + static_cast((value >> (2 * 8)) & 0xFF); + const unsigned char byte3 = + static_cast((value >> (3 * 8)) & 0xFF); + const unsigned char byte4 = + static_cast((value >> (4 * 8)) & 0xFF); + const unsigned char byte5 = + static_cast((value >> (5 * 8)) & 0xFF); + const unsigned char byte6 = + static_cast((value >> (6 * 8)) & 0xFF); + const unsigned char byte7 = + static_cast((value >> (7 * 8)) & 0xFF); checksumAppend(checksum, byte0); checksumAppend(checksum, byte1); @@ -5030,7 +5140,7 @@ void checksumAppend(CHECKSUM_ALGORITHM& checksum, } break; default: { BSLS_ASSERT(!"Unreachable"); - return; + return; // RETURN } break; } @@ -5280,7 +5390,7 @@ const unsigned char TestDataUtil::s_RANDOM_GARBAGE_1K[1024] = { 0x75, 0x6d, 0x88, 0xac }; -} // close u namespace +} // close namespace u } // close unnamed namespace // ============================================================================ @@ -5304,7 +5414,7 @@ int main(int argc, char *argv[]) bsls::ReviewFailureHandlerGuard reviewGuard(&bsls::Review::failByAbort); switch (test) { case 0: // Zero is always the leading case. - case 30: { + case 31: { // -------------------------------------------------------------------- // USAGE EXAMPLE // Extracted from component header file. @@ -5386,6 +5496,225 @@ int main(int argc, char *argv[]) if (verbose) bsl::cout << "\nEnd of test." << bsl::endl; } break; + case 30: { + // -------------------------------------------------------------------- + // CONCERN: TESTING +/- ZERO FLOATING-POINT + // + // Concerns: + //: 1 That the code correctly translates both positive and negative + //: zero values. + // + // Plan: + //: 1 Have two constant binary arrays, 'plusZeroSeq' and + //: 'minusZeroSeq', which contain representations of how positive and + //: negative zero are encoded by BER. + //: + //: 2 For both 'float' and 'double' types of floating-point variables, + //: o Use 'Util::putValue' to translate them both to output + //: 'streambuf's. + //: + //: o Verify that the output in the two streambufs correspond to + //: 'plusZeroSeq' and 'minusZeroSeq' as expected. + //: + //: o Load the two output sequences to two input 'streambuf's. + //: + //: o Use 'Util::getValue' to translate from the input 'streambuf's + //: to a different pair of floating-point varilables. + //: + //: o Verify that the values are as expected using + //: 'bdld::Float::classifyFine'. + //: + //: o Verify that the number of bytes consumed by 'getValue' is + //: correct. + //: + //: o Verify that the values produced exactly match the input values + //: using 'memcmp'. + // -------------------------------------------------------------------- + + if (verbose) cout << "CONCERN: TESTING +/- ZERO FLOATING-POINT\n" + "========================================\n"; + + typedef bdlb::Float Float; + + const char plusZeroSeq[] = { 0 }; + const char minusZeroSeq[] = { 1, 0x43 }; + + if (veryVerbose) cout << "float enabled\n"; + { + balber::BerEncoderOptions options; + options.setPreserveSignOfNegativeZero(true); + + bdlsb::MemOutStreamBuf osbX, osbY; + const float x = 0.0f, y = -0.0f; + ASSERT(x == y); + ASSERT(!(x > y) && !(y < x)); + ASSERT(bsl::memcmp(&x, &y, sizeof(x))); + + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(x)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(y)); + + ASSERT(0 == Util::putValue(&osbX, x, &options)); + ASSERT(0 == Util::putValue(&osbY, y, &options)); + + ASSERT(osbX.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbX.data(), plusZeroSeq, osbX.length())); + + ASSERT(osbY.length() == sizeof(minusZeroSeq)); + ASSERT(!bsl::memcmp(osbY.data(), minusZeroSeq, osbY.length())); + + bdlsb::FixedMemInStreamBuf isbX(osbX.data(), osbX.length()); + bdlsb::FixedMemInStreamBuf isbY(osbY.data(), osbY.length()); + + float xx = 10, yy = 10; + int xConsumed = 0, yConsumed = 0; + + ASSERT(0 == Util::getValue(&isbX, &xx, &xConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(xx)); + ASSERT(static_cast(sizeof(plusZeroSeq) == xConsumed)); + + ASSERT(0 == Util::getValue(&isbY, &yy, &yConsumed)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(yy)); + ASSERT(static_cast(sizeof(minusZeroSeq) == yConsumed)); + + ASSERT(!bsl::memcmp(&x, &xx, sizeof(x))); + ASSERT(!bsl::memcmp(&y, &yy, sizeof(y))); + + ASSERT(xx == yy); + ASSERT(!(xx > yy) && !(yy < xx)); + ASSERT(bsl::memcmp(&xx, &yy, sizeof(xx))); + } + + if (veryVerbose) cout << "double enabled\n"; + { + balber::BerEncoderOptions options; + options.setPreserveSignOfNegativeZero(true); + + bdlsb::MemOutStreamBuf osbX, osbY; + const double x = 0.0, y = -0.0; + ASSERT(x == y); + ASSERT(!(x > y) && !(y < x)); + ASSERT(bsl::memcmp(&x, &y, sizeof(x))); + + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(x)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(y)); + + ASSERT(0 == Util::putValue(&osbX, x, &options)); + ASSERT(0 == Util::putValue(&osbY, y, &options)); + + ASSERT(osbX.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbX.data(), plusZeroSeq, osbX.length())); + + ASSERT(osbY.length() == sizeof(minusZeroSeq)); + ASSERT(!bsl::memcmp(osbY.data(), minusZeroSeq, osbY.length())); + + bdlsb::FixedMemInStreamBuf isbX(osbX.data(), osbX.length()); + bdlsb::FixedMemInStreamBuf isbY(osbY.data(), osbY.length()); + + double xx = 10, yy = 10; + int xConsumed = 0, yConsumed = 0; + + ASSERT(0 == Util::getValue(&isbX, &xx, &xConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(xx)); + ASSERT(static_cast(sizeof(plusZeroSeq) == xConsumed)); + + ASSERT(0 == Util::getValue(&isbY, &yy, &yConsumed)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(yy)); + ASSERT(static_cast(sizeof(minusZeroSeq) == yConsumed)); + + ASSERT(!bsl::memcmp(&x, &xx, sizeof(x))); + ASSERT(!bsl::memcmp(&y, &yy, sizeof(y))); + + ASSERT(xx == yy); + ASSERT(!(xx > yy) && !(yy < xx)); + ASSERT(bsl::memcmp(&xx, &yy, sizeof(xx))); + } + + if (veryVerbose) cout << "float default (disabled)\n"; + { + bdlsb::MemOutStreamBuf osbX, osbY; + const float x = 0.0f, y = -0.0f; + ASSERT(x == y); + ASSERT(!(x > y) && !(y < x)); + ASSERT(bsl::memcmp(&x, &y, sizeof(x))); + + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(x)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(y)); + + ASSERT(0 == Util::putValue(&osbX, x)); + ASSERT(0 == Util::putValue(&osbY, y)); + + ASSERT(osbX.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbX.data(), plusZeroSeq, osbX.length())); + + ASSERT(osbY.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbY.data(), plusZeroSeq, osbY.length())); + + bdlsb::FixedMemInStreamBuf isbX(osbX.data(), osbX.length()); + bdlsb::FixedMemInStreamBuf isbY(osbY.data(), osbY.length()); + + float xx = 10, yy = 10; + int xConsumed = 0, yConsumed = 0; + + ASSERT(0 == Util::getValue(&isbX, &xx, &xConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(xx)); + ASSERT(static_cast(sizeof(plusZeroSeq) == xConsumed)); + + ASSERT(0 == Util::getValue(&isbY, &yy, &yConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(yy)); + ASSERT(static_cast(sizeof(plusZeroSeq) == yConsumed)); + + ASSERT(!bsl::memcmp(&x, &xx, sizeof(x))); + ASSERT( bsl::memcmp(&y, &yy, sizeof(y))); + ASSERT(!bsl::memcmp(&x, &yy, sizeof(x))); + + ASSERT(xx == yy); + ASSERT(!(xx > yy) && !(yy < xx)); + ASSERT(!bsl::memcmp(&xx, &yy, sizeof(xx))); + } + + if (veryVerbose) cout << "double default (disabled)\n"; + { + bdlsb::MemOutStreamBuf osbX, osbY; + const double x = 0.0, y = -0.0; + ASSERT(x == y); + ASSERT(!(x > y) && !(y < x)); + ASSERT(bsl::memcmp(&x, &y, sizeof(x))); + + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(x)); + ASSERT(Float::k_NEGATIVE_ZERO == Float::classifyFine(y)); + + ASSERT(0 == Util::putValue(&osbX, x)); + ASSERT(0 == Util::putValue(&osbY, y)); + + ASSERT(osbX.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbX.data(), plusZeroSeq, osbX.length())); + + ASSERT(osbY.length() == sizeof(plusZeroSeq)); + ASSERT(!bsl::memcmp(osbY.data(), plusZeroSeq, osbY.length())); + + bdlsb::FixedMemInStreamBuf isbX(osbX.data(), osbX.length()); + bdlsb::FixedMemInStreamBuf isbY(osbY.data(), osbY.length()); + + double xx = 10, yy = 10; + int xConsumed = 0, yConsumed = 0; + + ASSERT(0 == Util::getValue(&isbX, &xx, &xConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(xx)); + ASSERT(static_cast(sizeof(plusZeroSeq) == xConsumed)); + + ASSERT(0 == Util::getValue(&isbY, &yy, &yConsumed)); + ASSERT(Float::k_POSITIVE_ZERO == Float::classifyFine(yy)); + ASSERT(static_cast(sizeof(plusZeroSeq) == yConsumed)); + + ASSERT(!bsl::memcmp(&x, &xx, sizeof(x))); + ASSERT( bsl::memcmp(&y, &yy, sizeof(y))); + ASSERT(!bsl::memcmp(&x, &yy, sizeof(x))); + + ASSERT(xx == yy); + ASSERT(!(xx > yy) && !(yy < xx)); + ASSERT(!bsl::memcmp(&xx, &yy, sizeof(xx))); + } + } break; case 29: { // -------------------------------------------------------------------- // TESTING DATE/TIME FORMAT SELECTION @@ -5739,25 +6068,25 @@ int main(int argc, char *argv[]) { L_, 0, 3, F, 23, 59, 59, 999, 999,-1439, TXT, "\x12" "23:59:59.999-23:59" }, { L_, 0, 3, F, 24, 0, 0, 0, 0, 0, TXT, "\x12" "24:00:00.000+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0, 0, TXT, "\x15" "00:00:00.000000+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0, 1, TXT, "\x15" "00:00:00.000000+00:01" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0, -1, TXT, "\x15" "00:00:00.000000-00:01" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 1, 0, TXT, "\x15" "00:00:00.000001+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 1, 0, 0, TXT, "\x15" "00:00:00.001000+00:00" }, -{ L_, 0, 6, F, 0, 0, 1, 0, 0, 0, TXT, "\x15" "00:00:01.000000+00:00" }, -{ L_, 0, 6, F, 0, 1, 0, 0, 0, 0, TXT, "\x15" "00:01:00.000000+00:00" }, -{ L_, 0, 6, F, 1, 0, 0, 0, 0, 0, TXT, "\x15" "01:00:00.000000+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0, 1439, TXT, "\x15" "00:00:00.000000+23:59" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0,-1439, TXT, "\x15" "00:00:00.000000-23:59" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 0, 0, TXT, "\x15" "00:00:00.000000+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 0, 999, 0, TXT, "\x15" "00:00:00.000999+00:00" }, -{ L_, 0, 6, F, 0, 0, 0, 999, 0, 0, TXT, "\x15" "00:00:00.999000+00:00" }, -{ L_, 0, 6, F, 0, 0, 59, 0, 0, 0, TXT, "\x15" "00:00:59.000000+00:00" }, -{ L_, 0, 6, F, 0, 59, 0, 0, 0, 0, TXT, "\x15" "00:59:00.000000+00:00" }, -{ L_, 0, 6, F, 23, 0, 0, 0, 0, 0, TXT, "\x15" "23:00:00.000000+00:00" }, -{ L_, 0, 6, F, 23, 59, 59, 999, 999, 1439, TXT, "\x15" "23:59:59.999999+23:59" }, -{ L_, 0, 6, F, 23, 59, 59, 999, 999,-1439, TXT, "\x15" "23:59:59.999999-23:59" }, -{ L_, 0, 6, F, 24, 0, 0, 0, 0, 0, TXT, "\x15" "24:00:00.000000+00:00" }, +{ L_, 0, 6, F, 0, 0, 0, 0, 0, 0,TXT,"\x15" "00:00:00.000000+00:00"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 0, 1,TXT,"\x15" "00:00:00.000000+00:01"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 0, -1,TXT,"\x15" "00:00:00.000000-00:01"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 1, 0,TXT,"\x15" "00:00:00.000001+00:00"}, +{ L_, 0, 6, F, 0, 0, 0, 1, 0, 0,TXT,"\x15" "00:00:00.001000+00:00"}, +{ L_, 0, 6, F, 0, 0, 1, 0, 0, 0,TXT,"\x15" "00:00:01.000000+00:00"}, +{ L_, 0, 6, F, 0, 1, 0, 0, 0, 0,TXT,"\x15" "00:01:00.000000+00:00"}, +{ L_, 0, 6, F, 1, 0, 0, 0, 0, 0,TXT,"\x15" "01:00:00.000000+00:00"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 0, 1439,TXT,"\x15" "00:00:00.000000+23:59"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 0,-1439,TXT,"\x15" "00:00:00.000000-23:59"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 0, 0,TXT,"\x15" "00:00:00.000000+00:00"}, +{ L_, 0, 6, F, 0, 0, 0, 0, 999, 0,TXT,"\x15" "00:00:00.000999+00:00"}, +{ L_, 0, 6, F, 0, 0, 0, 999, 0, 0,TXT,"\x15" "00:00:00.999000+00:00"}, +{ L_, 0, 6, F, 0, 0, 59, 0, 0, 0,TXT,"\x15" "00:00:59.000000+00:00"}, +{ L_, 0, 6, F, 0, 59, 0, 0, 0, 0,TXT,"\x15" "00:59:00.000000+00:00"}, +{ L_, 0, 6, F, 23, 0, 0, 0, 0, 0,TXT,"\x15" "23:00:00.000000+00:00"}, +{ L_, 0, 6, F, 23, 59, 59, 999, 999, 1439,TXT,"\x15" "23:59:59.999999+23:59"}, +{ L_, 0, 6, F, 23, 59, 59, 999, 999,-1439,TXT,"\x15" "23:59:59.999999-23:59"}, +{ L_, 0, 6, F, 24, 0, 0, 0, 0, 0,TXT,"\x15" "24:00:00.000000+00:00"}, { L_, 0, 3, T, 0, 0, 0, 0, 0, 0, BIN, "01 00" }, { L_, 0, 3, T, 0, 0, 0, 0, 0, 1, BIN, "05 00 01 00 00 00" }, @@ -5821,25 +6150,25 @@ int main(int argc, char *argv[]) { L_, V , 3, F, 23, 59, 59, 999, 999,-1439, TXT, "\x12" "23:59:59.999-23:59" }, { L_, V , 3, F, 24, 0, 0, 0, 0, 0, TXT, "\x12" "24:00:00.000+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0, 0, TXT, "\x15" "00:00:00.000000+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0, 1, TXT, "\x15" "00:00:00.000000+00:01" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0, -1, TXT, "\x15" "00:00:00.000000-00:01" }, -{ L_, V , 6, F, 0, 0, 0, 0, 1, 0, TXT, "\x15" "00:00:00.000001+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 1, 0, 0, TXT, "\x15" "00:00:00.001000+00:00" }, -{ L_, V , 6, F, 0, 0, 1, 0, 0, 0, TXT, "\x15" "00:00:01.000000+00:00" }, -{ L_, V , 6, F, 0, 1, 0, 0, 0, 0, TXT, "\x15" "00:01:00.000000+00:00" }, -{ L_, V , 6, F, 1, 0, 0, 0, 0, 0, TXT, "\x15" "01:00:00.000000+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0, 1439, TXT, "\x15" "00:00:00.000000+23:59" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0,-1439, TXT, "\x15" "00:00:00.000000-23:59" }, -{ L_, V , 6, F, 0, 0, 0, 0, 0, 0, TXT, "\x15" "00:00:00.000000+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 0, 999, 0, TXT, "\x15" "00:00:00.000999+00:00" }, -{ L_, V , 6, F, 0, 0, 0, 999, 0, 0, TXT, "\x15" "00:00:00.999000+00:00" }, -{ L_, V , 6, F, 0, 0, 59, 0, 0, 0, TXT, "\x15" "00:00:59.000000+00:00" }, -{ L_, V , 6, F, 0, 59, 0, 0, 0, 0, TXT, "\x15" "00:59:00.000000+00:00" }, -{ L_, V , 6, F, 23, 0, 0, 0, 0, 0, TXT, "\x15" "23:00:00.000000+00:00" }, -{ L_, V , 6, F, 23, 59, 59, 999, 999, 1439, TXT, "\x15" "23:59:59.999999+23:59" }, -{ L_, V , 6, F, 23, 59, 59, 999, 999,-1439, TXT, "\x15" "23:59:59.999999-23:59" }, -{ L_, V , 6, F, 24, 0, 0, 0, 0, 0, TXT, "\x15" "24:00:00.000000+00:00" }, +{ L_, V , 6, F, 0, 0, 0, 0, 0, 0,TXT,"\x15" "00:00:00.000000+00:00"}, +{ L_, V , 6, F, 0, 0, 0, 0, 0, 1,TXT,"\x15" "00:00:00.000000+00:01"}, +{ L_, V , 6, F, 0, 0, 0, 0, 0, -1,TXT,"\x15" "00:00:00.000000-00:01"}, +{ L_, V , 6, F, 0, 0, 0, 0, 1, 0,TXT,"\x15" "00:00:00.000001+00:00"}, +{ L_, V , 6, F, 0, 0, 0, 1, 0, 0,TXT,"\x15" "00:00:00.001000+00:00"}, +{ L_, V , 6, F, 0, 0, 1, 0, 0, 0,TXT,"\x15" "00:00:01.000000+00:00"}, +{ L_, V , 6, F, 0, 1, 0, 0, 0, 0,TXT,"\x15" "00:01:00.000000+00:00"}, +{ L_, V , 6, F, 1, 0, 0, 0, 0, 0,TXT,"\x15" "01:00:00.000000+00:00"}, +{ L_, V , 6, F, 0, 0, 0, 0, 0, 1439,TXT,"\x15" "00:00:00.000000+23:59"}, +{ L_, V , 6, F, 0, 0, 0, 0, 0,-1439,TXT,"\x15" "00:00:00.000000-23:59"}, +{ L_, V , 6, F, 0, 0, 0, 0, 0, 0,TXT,"\x15" "00:00:00.000000+00:00"}, +{ L_, V , 6, F, 0, 0, 0, 0, 999, 0,TXT,"\x15" "00:00:00.000999+00:00"}, +{ L_, V , 6, F, 0, 0, 0, 999, 0, 0,TXT,"\x15" "00:00:00.999000+00:00"}, +{ L_, V , 6, F, 0, 0, 59, 0, 0, 0,TXT,"\x15" "00:00:59.000000+00:00"}, +{ L_, V , 6, F, 0, 59, 0, 0, 0, 0,TXT,"\x15" "00:59:00.000000+00:00"}, +{ L_, V , 6, F, 23, 0, 0, 0, 0, 0,TXT,"\x15" "23:00:00.000000+00:00"}, +{ L_, V , 6, F, 23, 59, 59, 999, 999, 1439,TXT,"\x15" "23:59:59.999999+23:59"}, +{ L_, V , 6, F, 23, 59, 59, 999, 999,-1439,TXT,"\x15" "23:59:59.999999-23:59"}, +{ L_, V , 6, F, 24, 0, 0, 0, 0, 0,TXT,"\x15" "24:00:00.000000+00:00"}, { L_, V , 3, T, 0, 0, 0, 0, 0, 0, BIN, "01 00" }, { L_, V , 3, T, 0, 0, 0, 0, 0, 1, BIN, "05 00 01 00 00 00" }, @@ -7100,8 +7429,8 @@ int main(int argc, char *argv[]) if (DATETIMETZ.localDatetime().time() == bdlt::Time() && VERSION < V && BINARY) { - const bdlt::Datetime& DATETIME = DATETIMETZ.localDatetime(); - const bdlt::Datetime& datetime = datetimeTz.localDatetime(); + const bdlt::Datetime& DATETIME =DATETIMETZ.localDatetime(); + const bdlt::Datetime& datetime =datetimeTz.localDatetime(); LOOP1_ASSERT_EQ(LINE, datetime.year(), DATETIME.year()); LOOP1_ASSERT_EQ(LINE, datetime.month(), DATETIME.month()); LOOP1_ASSERT_EQ(LINE, datetime.day(), DATETIME.day()); @@ -7540,12 +7869,12 @@ int main(int argc, char *argv[]) static const int MINUTES[] = { 0, 1, 2, 3, 32, 33, 57, 58, 59}; - static const int NUM_MINUTES = sizeof(MINUTES) / sizeof(MINUTES[0]); + static const int NUM_MINUTES = sizeof(MINUTES) / sizeof(*MINUTES); static const int SECONDS[] = { 0, 1, 2, 3, 4, 5, 31, 32, 33, 57, 58, 59}; - static const int NUM_SECONDS = sizeof(SECONDS) / sizeof(SECONDS[0]); + static const int NUM_SECONDS = sizeof(SECONDS) / sizeof(*SECONDS); static const int MILLISECONDS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 15, @@ -8101,12 +8430,12 @@ int main(int argc, char *argv[]) static const int MINUTES[] = { 0, 1, 2, 3, 32, 33, 57, 58, 59}; - static const int NUM_MINUTES = sizeof(MINUTES) / sizeof(MINUTES[0]); + static const int NUM_MINUTES = sizeof(MINUTES)/sizeof(MINUTES[0]); static const int SECONDS[] = { 0, 1, 2, 3, 4, 5, 31, 32, 33, 57, 58, 59}; - static const int NUM_SECONDS = sizeof(SECONDS) / sizeof(SECONDS[0]); + static const int NUM_SECONDS = sizeof(SECONDS)/sizeof(SECONDS[0]); static const int MILLISECONDS[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 15, @@ -9344,7 +9673,7 @@ int main(int argc, char *argv[]) static const int OFFSETS[] = { -1439, -1438, -1, 0, 1, 1438, 1439}; - static const int NUM_OFFSETS = sizeof(OFFSETS) / sizeof(OFFSETS[0]); + static const int NUM_OFFSETS = sizeof(OFFSETS)/sizeof(OFFSETS[0]); static const int NUM_DATA = NUM_YEARS * NUM_DAYS * NUM_HOURS * NUM_MINUTES * NUM_SECONDS * @@ -9783,6 +10112,7 @@ int main(int argc, char *argv[]) t(L_, bsl::numeric_limits::min()); t(L_, -1.f); t(L_, 0.f); + t(L_, -0.f); t(L_, 1.f); t(L_, bsl::numeric_limits::max()); @@ -9790,6 +10120,7 @@ int main(int argc, char *argv[]) t(L_, bsl::numeric_limits::min()); t(L_, 1.0); t(L_, 0.0); + t(L_, -0.0); t(L_, -1.0); t(L_, bsl::numeric_limits::max()); @@ -9851,10 +10182,11 @@ int main(int argc, char *argv[]) case 26: { // -------------------------------------------------------------------- // TESTING 'getValue' BEHAVIORAL FINGERPRINT + // // This case tests that the values decoded by // 'balber::BerUtil::getValue' for a large, deterministically and - // pseudo-randomly generated set of inputs, are *likely* equivalent to - // the values it decodes as of BDE 3.44.0. This case tests the + // pseudo-randomly generated set of inputs, are *likely* equivalent + // to the values it decodes as of BDE 3.44.0. This case tests the // decoded values for too many encodings to explicitly enumerate. // Instead, the encodings are generated during the execution of the // test, and the results of each invocation of 'getValue' are @@ -9922,31 +10254,49 @@ int main(int argc, char *argv[]) int d_numSamples; // number of input-output samples int d_secondPrecision; // an encoding option parameter bool d_encodeDateAndTimeTypesAsBinary; - // an encoding option parameter + // an encoding option parameter + bool d_preserveSignOfNegativeZero; + // an encoding option parameter const char *d_md5; // md5 fingerprint of all sampled output } DATA[] = { - // RANDOM SEED TO GENERATE INPUT FOR BEHAVIORS - // .------------------------------------------- - // / NUMBER OF SAMPLE BEHAVIORS IN FINGERPRINT - // / .----------------------------------------- - // / / FRACTIONAL SECOND PRECISION PARAM - // / / .--------------------------------- - // LINE / / / BINARY DATE AND TIME ENCODING PARAM - // .---- / / / .----------------------------------- - // / / / / / 'putValue' BEHAVIORAL FINGERPRINT - //-- ------- ------ -- ------ ------------------------------------ - { L_, SEED_0, 50000, 3, false, "a4f4796fce831c62afed26b178c63715" }, - { L_, SEED_0, 50000, 3, true , "a9e9d0fbbc1487449bf928907792f211" }, - { L_, SEED_0, 50000, 6, false, "0bab0341289bddcd8c66fd607b0b76dc" }, - { L_, SEED_0, 50000, 6, true , "a9e9d0fbbc1487449bf928907792f211" }, - { L_, SEED_1, 50000, 3, false, "53229ec3841b3815e8efb6cc8e64a098" }, - { L_, SEED_1, 50000, 3, true , "1c7ceb60dbd74c17be929311f86ab185" }, - { L_, SEED_1, 50000, 6, false, "4f884d423a3fbb65b531c5f4fe1ec0ed" }, - { L_, SEED_1, 50000, 6, true , "1c7ceb60dbd74c17be929311f86ab185" }, - { L_, SEED_2, 50000, 3, false, "01defb86e00fc10ca4c4a5dc802f9c54" }, - { L_, SEED_2, 50000, 3, true , "de75fb921b25090f0f6975b6e4bf8bd3" }, - { L_, SEED_2, 50000, 6, false, "af150f3a022e5fd55ccb5b400bfbc487" }, - { L_, SEED_2, 50000, 6, true , "de75fb921b25090f0f6975b6e4bf8bd3" }, + // RANDOM SEED TO GENERATE INPUT FOR BEHAVIORS + // .------------------------------------------- + // / NUMBER OF SAMPLE BEHAVIORS IN FINGERPRINT + // / .----------------------------------------- + // / / FRACTIONAL SECOND PRECISION PARAM + // / / .--------------------------------- + // LINE / / / BINARY DATE AND TIME ENCODING PARAM + // .---- / / / .----------------------------------- + // / / / / / PRESERVE -0.0 + // / / / / / .------------- + // / / / / / / + // / / / / / / 'putValue' BEHAVIORAL FINGERPRINT + //-- ------- ------ -- -- -- ------------------------------------ + { L_, SEED_0, 50000, 3, 0, 0, "a4f4796fce831c62afed26b178c63715"}, + { L_, SEED_0, 50000, 3, 1, 0, "a9e9d0fbbc1487449bf928907792f211"}, + { L_, SEED_0, 50000, 6, 0, 0, "0bab0341289bddcd8c66fd607b0b76dc"}, + { L_, SEED_0, 50000, 6, 1, 0, "a9e9d0fbbc1487449bf928907792f211"}, + { L_, SEED_1, 50000, 3, 0, 0, "53229ec3841b3815e8efb6cc8e64a098"}, + { L_, SEED_1, 50000, 3, 1, 0, "1c7ceb60dbd74c17be929311f86ab185"}, + { L_, SEED_1, 50000, 6, 0, 0, "4f884d423a3fbb65b531c5f4fe1ec0ed"}, + { L_, SEED_1, 50000, 6, 1, 0, "1c7ceb60dbd74c17be929311f86ab185"}, + { L_, SEED_2, 50000, 3, 0, 0, "01defb86e00fc10ca4c4a5dc802f9c54"}, + { L_, SEED_2, 50000, 3, 1, 0, "de75fb921b25090f0f6975b6e4bf8bd3"}, + { L_, SEED_2, 50000, 6, 0, 0, "af150f3a022e5fd55ccb5b400bfbc487"}, + { L_, SEED_2, 50000, 6, 1, 0, "de75fb921b25090f0f6975b6e4bf8bd3"}, + + { L_, SEED_0, 50000, 3, 0, 1, "37539c57e7dc72c1a2d24cf56d1d96e2"}, + { L_, SEED_0, 50000, 3, 1, 1, "f937612db9bc8da228655bd470918255"}, + { L_, SEED_0, 50000, 6, 0, 1, "08631567916b5a064865038a96028a2d"}, + { L_, SEED_0, 50000, 6, 1, 1, "f937612db9bc8da228655bd470918255"}, + { L_, SEED_1, 50000, 3, 0, 1, "e4adee9271c3df00fb3aa1ef75421ca0"}, + { L_, SEED_1, 50000, 3, 1, 1, "712272f288c9b4019f6c214b71ca67dc"}, + { L_, SEED_1, 50000, 6, 0, 1, "b19cfc128a5b0da24987bb5679c1e055"}, + { L_, SEED_1, 50000, 6, 1, 1, "712272f288c9b4019f6c214b71ca67dc"}, + { L_, SEED_2, 50000, 3, 0, 1, "cb15f9619e5f33bfc8c7c8b1b9d8e704"}, + { L_, SEED_2, 50000, 3, 1, 1, "16dfbfa01e9facac7370aa9b535cd84a"}, + { L_, SEED_2, 50000, 6, 0, 1, "c74d4bc9b8567c6dbce305163a78d9a8"}, + { L_, SEED_2, 50000, 6, 1, 1, "16dfbfa01e9facac7370aa9b535cd84a"}, }; static const int NUM_DATA = sizeof DATA / sizeof *DATA; @@ -9957,7 +10307,9 @@ int main(int argc, char *argv[]) const int NUM_SAMPLES = DATA[i].d_numSamples; const int SECOND_PRECISION = DATA[i].d_secondPrecision; const bool ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = - DATA[i].d_encodeDateAndTimeTypesAsBinary; + DATA[i].d_encodeDateAndTimeTypesAsBinary; + const bool PRESERVE_SIGN_OF_NEGATIVE_ZERO = + DATA[i].d_preserveSignOfNegativeZero; const bslstl::StringRef MD5 = DATA[i].d_md5; u::GetValueFingerprint getValueFingerprint; @@ -9965,7 +10317,9 @@ int main(int argc, char *argv[]) getValueFingerprint.setNumSamples(NUM_SAMPLES); getValueFingerprint.setFractionalSecondPrecision(SECOND_PRECISION); getValueFingerprint.setEncodeDateAndTimeTypesAsBinary( - ENCODE_DATE_AND_TIME_TYPES_AS_BINARY); + ENCODE_DATE_AND_TIME_TYPES_AS_BINARY); + getValueFingerprint.setPreserveSignOfNegativeZero( + PRESERVE_SIGN_OF_NEGATIVE_ZERO); const u::Md5Fingerprint md5Fingerprint = u::ChecksumUtil::getMd5(getValueFingerprint); @@ -9974,7 +10328,7 @@ int main(int argc, char *argv[]) bsl::ostream md5FingerprintStream(&md5FingerprintStreamBuf); md5FingerprintStream << md5Fingerprint; - const bslstl::StringRef md5FingerprintString( + const bsl::string_view md5FingerprintString( md5FingerprintStreamBuf.data(), md5FingerprintStreamBuf.length()); @@ -10055,31 +10409,48 @@ int main(int argc, char *argv[]) int d_numSamples; // number of input-output samples int d_secondPrecision; // an encoding option parameter bool d_encodeDateAndTimeTypesAsBinary; - // an encoding option parameter + // an encoding option parameter + bool d_preserveSignOfNegativeZero; + // an encoding option parameter const char *d_md5; // md5 fingerprint of all sampled output } DATA[] = { - // RANDOM SEED TO GENERATE INPUT FOR BEHAVIORS - // .------------------------------------------- - // / NUMBER OF SAMPLE BEHAVIORS IN FINGERPRINT - // / .----------------------------------------- - // / / FRACTIONAL SECOND PRECISION - // / / .--------------------------- - // LINE / / / USE BINARY DATE AND TIME ENCODING - // .---- / / / .--------------------------------- - // / / / / / 'putValue' BEHAVIORAL FINGERPRINT - //-- ------- ------ -- ------ ------------------------------------ - { L_, SEED_0, 50000, 3, false, "a893e5c4643b5b40b45aa8d93c90a097" }, - { L_, SEED_0, 50000, 3, true , "7166428b5ca3e18a0953877091f37ce7" }, - { L_, SEED_0, 50000, 6, false, "95acf3bfe61bed5bf29c686c61ff6269" }, - { L_, SEED_0, 50000, 6, true , "7166428b5ca3e18a0953877091f37ce7" }, - { L_, SEED_1, 50000, 3, false, "37ce54c6d2f92fd9a822080aeda006e2" }, - { L_, SEED_1, 50000, 3, true , "d0a8c8d46f37a89f15e71dae0c64d492" }, - { L_, SEED_1, 50000, 6, false, "9d3d66bd3b64fc76d51ba638c2d88531" }, - { L_, SEED_1, 50000, 6, true , "d0a8c8d46f37a89f15e71dae0c64d492" }, - { L_, SEED_2, 50000, 3, false, "06c17b7af732eaa78f2fb8a03351d0fc" }, - { L_, SEED_2, 50000, 3, true , "2689b7bf2a0a5002170e1c631fdf29ef" }, - { L_, SEED_2, 50000, 6, false, "b3bf9ce8ffa3a8601e8edf915b8c418a" }, - { L_, SEED_2, 50000, 6, true , "2689b7bf2a0a5002170e1c631fdf29ef" }, + // RANDOM SEED TO GENERATE INPUT FOR BEHAVIORS + // .------------------------------------------- + // / NUMBER OF SAMPLE BEHAVIORS IN FINGERPRINT + // / .----------------------------------------- + // / / FRACTIONAL SECOND PRECISION + // / / .--------------------------- + // LINE / / / USE BINARY DATE AND TIME ENCODING + // .---- / / / .--------------------------------- + // / / / / / SUPPORT NEGATIVE ZERO + // / / / / / .---------------------- + // / / / / / / 'putValue' BEHAVIORAL FINGERPRINT + //-- ------- ------ -- -- -- ------------------------------------ + { L_, SEED_0, 50000, 3, 0, 0, "a893e5c4643b5b40b45aa8d93c90a097"}, + { L_, SEED_0, 50000, 3, 1, 0, "7166428b5ca3e18a0953877091f37ce7"}, + { L_, SEED_0, 50000, 6, 0, 0, "95acf3bfe61bed5bf29c686c61ff6269"}, + { L_, SEED_0, 50000, 6, 1, 0, "7166428b5ca3e18a0953877091f37ce7"}, + { L_, SEED_1, 50000, 3, 0, 0, "37ce54c6d2f92fd9a822080aeda006e2"}, + { L_, SEED_1, 50000, 3, 1, 0, "d0a8c8d46f37a89f15e71dae0c64d492"}, + { L_, SEED_1, 50000, 6, 0, 0, "9d3d66bd3b64fc76d51ba638c2d88531"}, + { L_, SEED_1, 50000, 6, 1, 0, "d0a8c8d46f37a89f15e71dae0c64d492"}, + { L_, SEED_2, 50000, 3, 0, 0, "06c17b7af732eaa78f2fb8a03351d0fc"}, + { L_, SEED_2, 50000, 3, 1, 0, "2689b7bf2a0a5002170e1c631fdf29ef"}, + { L_, SEED_2, 50000, 6, 0, 0, "b3bf9ce8ffa3a8601e8edf915b8c418a"}, + { L_, SEED_2, 50000, 6, 1, 0, "2689b7bf2a0a5002170e1c631fdf29ef"}, + + { L_, SEED_0, 50000, 3, 0, 1, "68846207e44b5fada3d6b2cff88ef770"}, + { L_, SEED_0, 50000, 3, 1, 1, "e375e85611e37c9a83ac634a7153c983"}, + { L_, SEED_0, 50000, 6, 0, 1, "b4f68d43d9592546594acc3c7afd7c88"}, + { L_, SEED_0, 50000, 6, 1, 1, "e375e85611e37c9a83ac634a7153c983"}, + { L_, SEED_1, 50000, 3, 0, 1, "082c2fd9d76c0b80f598af7ca6db3060"}, + { L_, SEED_1, 50000, 3, 1, 1, "56abf2c67955f1abe22aa7b253881a4a"}, + { L_, SEED_1, 50000, 6, 0, 1, "9bc68a0d1ea0d9e379429aa35d54c6ae"}, + { L_, SEED_1, 50000, 6, 1, 1, "56abf2c67955f1abe22aa7b253881a4a"}, + { L_, SEED_2, 50000, 3, 0, 1, "c6ce8388750392e441abd8183e9ca49d"}, + { L_, SEED_2, 50000, 3, 1, 1, "f30b460da4bd4ad1682ba02433f96f67"}, + { L_, SEED_2, 50000, 6, 0, 1, "1ee257306330a981d902d64616716235"}, + { L_, SEED_2, 50000, 6, 1, 1, "f30b460da4bd4ad1682ba02433f96f67"}, }; static const int NUM_DATA = sizeof DATA / sizeof *DATA; @@ -10090,7 +10461,9 @@ int main(int argc, char *argv[]) const int NUM_SAMPLES = DATA[i].d_numSamples; const int SECOND_PRECISION = DATA[i].d_secondPrecision; const bool ENCODE_DATE_AND_TIME_TYPES_AS_BINARY = - DATA[i].d_encodeDateAndTimeTypesAsBinary; + DATA[i].d_encodeDateAndTimeTypesAsBinary; + const bool PRESERVE_SIGN_OF_NEGATIVE_ZERO = + DATA[i].d_preserveSignOfNegativeZero; const bslstl::StringRef MD5 = DATA[i].d_md5; u::PutValueFingerprint putValueFingerprint; @@ -10098,7 +10471,9 @@ int main(int argc, char *argv[]) putValueFingerprint.setNumSamples(NUM_SAMPLES); putValueFingerprint.setFractionalSecondPrecision(SECOND_PRECISION); putValueFingerprint.setEncodeDateAndTimeTypesAsBinary( - ENCODE_DATE_AND_TIME_TYPES_AS_BINARY); + ENCODE_DATE_AND_TIME_TYPES_AS_BINARY); + putValueFingerprint.setPreserveSignOfNegativeZero( + PRESERVE_SIGN_OF_NEGATIVE_ZERO); const u::Md5Fingerprint md5Fingerprint = u::ChecksumUtil::getMd5(putValueFingerprint); @@ -10107,7 +10482,7 @@ int main(int argc, char *argv[]) bsl::ostream md5FingerprintStream(&md5FingerprintStreamBuf); md5FingerprintStream << md5Fingerprint; - const bslstl::StringRef md5FingerprintString( + const bsl::string_view md5FingerprintString( md5FingerprintStreamBuf.data(), md5FingerprintStreamBuf.length()); @@ -10433,19 +10808,22 @@ int main(int argc, char *argv[]) // Create 2 masks. One for a single bit in the byte at the // current index, the other for all but that one bit. const unsigned char bitMask = - static_cast(0x01 << (i % 8)); - const unsigned char byteMask = ~bitMask; + static_cast(0x01 << (i % 8)); + const unsigned char byteMask = + static_cast(~bitMask); // Use the single-bit mask to get a byte having all bits 0, // except for the masked bit from the byte at the current // index (which may be 0 or 1). - const unsigned char bitAtIdx = byteAtIdx & bitMask; + const unsigned char bitAtIdx = static_cast( + byteAtIdx & bitMask); // Calculate the unsigned char value having the same value // as 'byteAtIdx' except for having exactly 1 of its 8 bits // flipped. - const unsigned char newByteAtIdx = (byteAtIdx & byteMask) | - (~bitAtIdx & bitMask); + const unsigned char newByteAtIdx = static_cast( + (byteAtIdx & byteMask) | + (~bitAtIdx & bitMask)); // Replace the byte at the specified index with the above // calculated value, thereby flipping a single bit in the @@ -13357,17 +13735,17 @@ int main(int argc, char *argv[]) ASSERT(0 == Util::putValue(&osb1, VALUE1, &options)); - const int LENGTH1 = static_cast(osb1.length()); + const int LENGTH1 =static_cast(osb1.length()); ASSERT(0 == Util::putValue(&osb2, VALUE2, &options)); - const int LENGTH2 = static_cast(osb2.length()); + const int LENGTH2 =static_cast(osb2.length()); ASSERT(0 == Util::putValue(&osb3, VALUE3, &options)); - const int LENGTH3 = static_cast(osb3.length()); + const int LENGTH3 =static_cast(osb3.length()); if (veryVerbose) { cout << "Output Buffer:"; @@ -13464,17 +13842,17 @@ int main(int argc, char *argv[]) ASSERT(0 == Util::putValue(&osb1, VALUE1, &DEFOPTS)); - const int LENGTH1 = static_cast(osb1.length()); + const int LENGTH1 =static_cast(osb1.length()); ASSERT(0 == Util::putValue(&osb2, VALUE2, &DEFOPTS)); - const int LENGTH2 = static_cast(osb2.length()); + const int LENGTH2 =static_cast(osb2.length()); ASSERT(0 == Util::putValue(&osb3, VALUE3, &DEFOPTS)); - const int LENGTH3 = static_cast(osb3.length()); + const int LENGTH3 =static_cast(osb3.length()); if (veryVerbose) { cout << "Output Buffer:"; @@ -13617,17 +13995,17 @@ int main(int argc, char *argv[]) ASSERT(0 == Util::putValue(&osb1, VALUE1, &options)); - const int LENGTH1 = static_cast(osb1.length()); + const int LENGTH1 =static_cast(osb1.length()); ASSERT(0 == Util::putValue(&osb2, VALUE2, &options)); - const int LENGTH2 = static_cast(osb2.length()); + const int LENGTH2 =static_cast(osb2.length()); ASSERT(0 == Util::putValue(&osb3, VALUE3, &options)); - const int LENGTH3 = static_cast(osb3.length()); + const int LENGTH3 =static_cast(osb3.length()); if (veryVerbose) { cout << "Output Buffer:"; @@ -13691,17 +14069,17 @@ int main(int argc, char *argv[]) ASSERT(0 == Util::putValue(&osb1, VALUE1, &DEFOPTS)); - const int LENGTH1 = static_cast(osb1.length()); + const int LENGTH1 =static_cast(osb1.length()); ASSERT(0 == Util::putValue(&osb2, VALUE2, &DEFOPTS)); - const int LENGTH2 = static_cast(osb2.length()); + const int LENGTH2 =static_cast(osb2.length()); ASSERT(0 == Util::putValue(&osb3, VALUE3, &DEFOPTS)); - const int LENGTH3 = static_cast(osb3.length()); + const int LENGTH3 =static_cast(osb3.length()); if (veryVerbose) { cout << "Output Buffer:"; @@ -14975,44 +15353,47 @@ int main(int argc, char *argv[]) if (verbose) bsl::cout << "\nTESTING 'putValue', 'getValue' for string" << "\n=========================================" << bsl::endl; - { - static const struct { - int d_lineNum; // source line number - const char *d_string; // string value - const char *d_exp; // expected value - } DATA[] = { - //line no. string exp - //------- ------ --- - { L_, "", "00" }, - { L_, " ", "01 20" }, - { L_, "-+", "02 2D 2B" }, - { L_, "Hello", "05 48 65 6C 6C 6F" }, - { L_, "12345", "05 31 32 33 34 35" }, - { L_, "&$#", "03 26 24 23" }, - { L_, "Have a nice day", "0F 48 61 76 65 20 61 20 6E" - "69 63 65 20 64 61 79" }, - { L_, "QWERTY", "06 51 57 45 52 54 59" }, - { L_, "This is an extremely long line that spans " - "a few lines", "35 54 68 69 73 20 69 73 20" - "61 6E 20 65 78 74 72 65 6D" - "65 6C 79 20 6C 6F 6E 67 20" - "6C 69 6E 65 20 74 68 61 74" - "20 73 70 61 6E 73 20 61 20" - "66 65 77 20 6C 69 6E 65 73" }, - }; - const int NUM_DATA = sizeof DATA / sizeof *DATA; + static const struct { + int d_lineNum; // source line number + const char *d_string; // string value + const char *d_exp; // expected value + } DATA[] = { + //line no. string exp + //------- ------ --- + { L_, "", "00" }, + { L_, " ", "01 20" }, + { L_, "-+", "02 2D 2B" }, + { L_, "Hello", "05 48 65 6C 6C 6F" }, + { L_, "12345", "05 31 32 33 34 35" }, + { L_, "&$#", "03 26 24 23" }, + { L_, "Have a nice day", "0F 48 61 76 65 20 61 20 6E" + "69 63 65 20 64 61 79" }, + { L_, "QWERTY", "06 51 57 45 52 54 59" }, + { L_, "This is an extremely long line that spans " + "a few lines", "35 54 68 69 73 20 69 73 20" + "61 6E 20 65 78 74 72 65 6D" + "65 6C 79 20 6C 6F 6E 67 20" + "6C 69 6E 65 20 74 68 61 74" + "20 73 70 61 6E 73 20 61 20" + "66 65 77 20 6C 69 6E 65 73" }, + }; + + const int NUM_DATA = sizeof DATA / sizeof *DATA; - for (int di = 0; di < NUM_DATA; ++di) { - const int LINE = DATA[di].d_lineNum; - const StringRef VAL = DATA[di].d_string; - const char *EXP = DATA[di].d_exp; - const int LEN = numOctets(EXP); + for (int di = 0; di < NUM_DATA; ++di) { + const int LINE = DATA[di].d_lineNum; + const bslstl::StringRef SR = DATA[di].d_string; + const bsl::string S = DATA[di].d_string; + const char *EXP = DATA[di].d_exp; + const int LEN = numOctets(EXP); + if (veryVerbose) Q(StringRef); + { string val; bdlsb::MemOutStreamBuf osb; - LOOP_ASSERT(LINE, 0 == Util::putValue(&osb, VAL)); + LOOP_ASSERT(LINE, 0 == Util::putValue(&osb, SR)); LOOP_ASSERT(LINE, LEN == (int)osb.length()); LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), EXP)); @@ -15025,10 +15406,40 @@ int main(int argc, char *argv[]) int numBytesConsumed = 0; bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length()); LOOP_ASSERT(LINE, - SUCCESS == - Util::getValue(&isb, &val, &numBytesConsumed)); + SUCCESS == Util::getValue(&isb, + &val, + &numBytesConsumed)); LOOP_ASSERT(LINE, 0 == isb.length()); - LOOP2_ASSERT(VAL, val, VAL == val); + LOOP2_ASSERT(SR, val, SR == val); + LOOP3_ASSERT(LINE, + LEN, + numBytesConsumed, + LEN == numBytesConsumed); + } + + if (veryVerbose) Q(bsl::string); + { + string val; + + bdlsb::MemOutStreamBuf osb; + LOOP_ASSERT(LINE, 0 == Util::putValue(&osb, S)); + LOOP_ASSERT(LINE, LEN == (int)osb.length()); + LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), EXP)); + + if (veryVerbose) { + P(EXP) + cout << "Output Buffer:"; + printBuffer(osb.data(), osb.length()); + } + + int numBytesConsumed = 0; + bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length()); + LOOP_ASSERT(LINE, + SUCCESS == Util::getValue(&isb, + &val, + &numBytesConsumed)); + LOOP_ASSERT(LINE, 0 == isb.length()); + LOOP2_ASSERT(S, val, S == val); LOOP3_ASSERT(LINE, LEN, numBytesConsumed, @@ -15050,62 +15461,60 @@ int main(int argc, char *argv[]) if (verbose) bsl::cout << "\nTESTING 'putValue', 'getValue' for string" << "\n=========================================" << bsl::endl; - { - static const struct { - int d_lineNum; // source line number - const char *d_string; // string value - const char *d_exp; // expected value - } DATA[] = { - //line no. string exp - //------- ------ --- - { L_, "", "00" }, - { L_, " ", "01 20" }, - { L_, "-+", "02 2D 2B" }, - { L_, "Hello", "05 48 65 6C 6C 6F" }, - { L_, "12345", "05 31 32 33 34 35" }, - { L_, "&$#", "03 26 24 23" }, - { L_, "Have a nice day", "0F 48 61 76 65 20 61 20 6E" - "69 63 65 20 64 61 79" }, - { L_, "QWERTY", "06 51 57 45 52 54 59" }, - { L_, "This is an extremely long line that spans " - "a few lines", "35 54 68 69 73 20 69 73 20" - "61 6E 20 65 78 74 72 65 6D" - "65 6C 79 20 6C 6F 6E 67 20" - "6C 69 6E 65 20 74 68 61 74" - "20 73 70 61 6E 73 20 61 20" - "66 65 77 20 6C 69 6E 65 73" }, - }; + struct Data { + int d_lineNum; // source line number + const char *d_string; // string value + const char *d_exp; // expected value + } DATA[] = { + //line no. string exp + //------- ------ --- + { L_, "", "00" }, + { L_, " ", "01 20" }, + { L_, "-+", "02 2D 2B" }, + { L_, "Hello", "05 48 65 6C 6C 6F" }, + { L_, "12345", "05 31 32 33 34 35" }, + { L_, "&$#", "03 26 24 23" }, + { L_, "Have a nice day", "0F 48 61 76 65 20 61 20 6E" + "69 63 65 20 64 61 79" }, + { L_, "QWERTY", "06 51 57 45 52 54 59" }, + { L_, "This is an extremely long line that spans " + "a few lines", "35 54 68 69 73 20 69 73 20" + "61 6E 20 65 78 74 72 65 6D" + "65 6C 79 20 6C 6F 6E 67 20" + "6C 69 6E 65 20 74 68 61 74" + "20 73 70 61 6E 73 20 61 20" + "66 65 77 20 6C 69 6E 65 73" }, + }; + enum { NUM_DATA = sizeof DATA / sizeof *DATA }; - const int NUM_DATA = sizeof DATA / sizeof *DATA; + for (int di = 0; di < NUM_DATA; ++di) { + const Data& data = DATA[di]; + const int LINE = data.d_lineNum; + const bsl::string VAL = data.d_string; + const char *EXP = data.d_exp; + const int LEN = numOctets(EXP); - for (int di = 0; di < NUM_DATA; ++di) { - const int LINE = DATA[di].d_lineNum; - const string VAL = DATA[di].d_string; - const char *EXP = DATA[di].d_exp; - const int LEN = numOctets(EXP); - string val; + bsl::string val; - bdlsb::MemOutStreamBuf osb; - LOOP_ASSERT(LINE, 0 == Util::putValue(&osb, VAL)); - LOOP_ASSERT(LINE, LEN == (int)osb.length()); - LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), EXP)); - - if (veryVerbose) { - P(EXP) - cout << "Output Buffer:"; - printBuffer(osb.data(), osb.length()); - } + bdlsb::MemOutStreamBuf osb; + LOOP_ASSERT(LINE, 0 == Util::putValue(&osb, VAL)); + LOOP_ASSERT(LINE, LEN == (int)osb.length()); + LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), EXP)); - int numBytesConsumed = 0; - bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length()); - LOOP_ASSERT(LINE, - SUCCESS == - Util::getValue(&isb, &val, &numBytesConsumed)); - LOOP_ASSERT(LINE, 0 == isb.length()); - LOOP2_ASSERT(VAL, val, VAL == val); - LOOP3_ASSERT(LINE, LEN, numBytesConsumed, - LEN == numBytesConsumed); + if (veryVerbose) { + P(EXP) + cout << "Output Buffer:"; + printBuffer(osb.data(), osb.length()); } + + int numBytesConsumed = 0; + bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length()); + LOOP_ASSERT(LINE, + SUCCESS == Util::getValue(&isb, &val, &numBytesConsumed)); + LOOP_ASSERT(LINE, 0 == isb.length()); + LOOP2_ASSERT(VAL, val, VAL == val); + LOOP3_ASSERT(LINE, LEN, numBytesConsumed, + LEN == numBytesConsumed); } } break; case 12: { @@ -15286,10 +15695,14 @@ int main(int argc, char *argv[]) if (veryVerbose) { P_(i) P(VAL) } { + balber::BerEncoderOptions options; + options.setPreserveSignOfNegativeZero(true); bdlsb::MemOutStreamBuf osb; LOOP_ASSERT(LINE, SUCCESS == - DoubleUtil::putDoubleValue(&osb, VAL)); + DoubleUtil::putDoubleValue(&osb, + VAL, + &options)); LOOP_ASSERT(LINE, LEN == (int)osb.length()); LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), EXP)); @@ -15361,7 +15774,7 @@ int main(int argc, char *argv[]) // +ve & -ve 0 { L_, 0, 0, 0, "00", SUCCESS }, - { L_, 1, 0, 0, "00", SUCCESS }, + { L_, 1, 0, 0, "01 43", SUCCESS }, // Denormalized numbers { L_, 0, 0, 1, "04 81 FB CE 01", @@ -15392,9 +15805,13 @@ int main(int argc, char *argv[]) assembleDouble(&outVal, SIGN, EXPONENT, MANTISSA); + balber::BerEncoderOptions options; + options.setPreserveSignOfNegativeZero(true); LOOP_ASSERT(LINE, RESULT == - DoubleUtil::putDoubleValue(&osb, outVal)); + DoubleUtil::putDoubleValue(&osb, + outVal, + &options)); if (SUCCESS == RESULT) { LOOP_ASSERT(LINE, LEN == (int)osb.length()); LOOP_ASSERT(LINE, 0 == compareBuffers(osb.data(), @@ -16071,7 +16488,7 @@ int main(int argc, char *argv[]) bdlsb::FixedMemInStreamBuf isb(osb.data(), osb.length()); LOOP_ASSERT(LINE, SUCCESS == - IntegerUtil::getIntegerValue(&vi, &isb, LEN)); + IntegerUtil::getIntegerValue(&vi, &isb, LEN)); LOOP_ASSERT(LINE, 0 == isb.length()); LOOP_ASSERT(LINE, vi == VI); } @@ -16482,15 +16899,15 @@ int main(int argc, char *argv[]) { L_, 8388608, 4 }, { L_, 2147483646, 4 }, { L_, INT_MAX, 4 }, - { L_, 0x7FFFFFFFFFLL, 5 }, - { L_, 0x8000000000LL, 6 }, - { L_, 0x7FFFFFFFFFFFLL, 6 }, - { L_, 0x800000000000LL, 7 }, - { L_, 0x7FFFFFFFFFFFFFLL, 7 }, - { L_, 0x80000000000000LL, 8 }, - { L_, 0x7FFFFFFFFFFFFFFFLL, 8 }, - { L_, 0x8000000000000000LL, 9 }, - { L_, 0xFFFFFFFFFFFFFFFFLL, 9 }, + { L_, 0x7FFFFFFFFFULL, 5 }, + { L_, 0x8000000000ULL, 6 }, + { L_, 0x7FFFFFFFFFFFULL, 6 }, + { L_, 0x800000000000ULL, 7 }, + { L_, 0x7FFFFFFFFFFFFFULL, 7 }, + { L_, 0x80000000000000ULL, 8 }, + { L_, 0x7FFFFFFFFFFFFFFFULL, 8 }, + { L_, 0x8000000000000000ULL, 9 }, + { L_, 0xFFFFFFFFFFFFFFFFULL, 9 }, }; const int NUM_DATA = sizeof(DATA) / sizeof(*DATA); diff --git a/groups/bal/balcl/balcl_commandline.cpp b/groups/bal/balcl/balcl_commandline.cpp index dfb7245d84..4a722f181c 100644 --- a/groups/bal/balcl/balcl_commandline.cpp +++ b/groups/bal/balcl/balcl_commandline.cpp @@ -16,6 +16,7 @@ BSLS_IDENT_RCSID(balcl_commandline_cpp,"$Id$ $CSID$") #include #include +#include #include #include @@ -165,6 +166,7 @@ Ordinal::Ordinal(bsl::size_t n) bsl::ostream& u::operator<<(bsl::ostream& stream, Ordinal position) { // ranks start at 0, but are displayed as 1st, 2nd, etc. + int n = static_cast(position.d_rank + 1); switch (n % 10) { case 1: { @@ -202,80 +204,83 @@ namespace u { // ===================== struct OptionValueUtil { - static void setValue(void *dst, const OptionValue& src); + static void setLinkedVariableValue( + void *dst, + bool isOptionalLinkedVariable, + const OptionValue& src); // Assign to the object at the specified 'dst' the value of the // specified 'src'. The behavior is undefined unless - // 'OptionType::e_VOID != src.type()' and 'dst' can be cast to a - // pointer to an object of 'OptionType::EnumToType::type'. + // 'OptionType::e_VOID != src.type()' and 'dst' can be legally cast to + // a 'OptionType::EnumToType::type *' or, if the specified + // 'isOptionalLinkedVariable' is 'true', to + // 'bsl::optional::type> *'. }; // --------------------- // class OptionValueUtil // --------------------- -void OptionValueUtil::setValue(void *dst, - const OptionValue& src) +void OptionValueUtil::setLinkedVariableValue( + void *dst, + bool isOptionalLinkedVariable, + const OptionValue& src) { BSLS_ASSERT(dst); typedef OptionType Ot; +#define CASE(ENUM) \ + case ENUM: { \ + typedef Ot::EnumToType::type Type; \ + \ + *(static_cast(dst)) = src.the(); \ + } break; \ + +#define CASE_MAYBE_OPTIONAL_LINK(ENUM) \ + case ENUM: { \ + typedef Ot::EnumToType::type Type; \ + \ + if (isOptionalLinkedVariable) { \ + BSLS_ASSERT(Ot::e_BOOL != src.type()); \ + BSLS_ASSERT(false == Ot::isArrayType(src.type())); \ + \ + typedef bsl::optional OptType; \ + \ + *(static_cast(dst)) = src.the(); \ + } else { \ + *(static_cast< Type *>(dst)) = src.the(); \ + } \ + } break; \ + switch (src.type()) { case Ot::e_VOID: { - BSLS_ASSERT_OPT(!"Reached"); - } break; - case Ot::e_BOOL: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_CHAR: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_INT: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_INT64: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_DOUBLE: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_STRING: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_DATETIME: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_DATE: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_TIME: { - *(static_cast(dst)) = src.the(); + BSLS_ASSERT_OPT(!"Reached"); } break; - case Ot::e_CHAR_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_INT_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_INT64_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_DOUBLE_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_STRING_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_DATETIME_ARRAY: { - *(static_cast(dst)) = - src.the(); - } break; - case Ot::e_DATE_ARRAY: { - *(static_cast(dst)) = src.the(); - } break; - case Ot::e_TIME_ARRAY: { - *(static_cast(dst)) = src.the(); + + CASE (Ot::e_BOOL) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_CHAR) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_INT) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_INT64) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_DOUBLE) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_STRING) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_DATETIME) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_DATE) + CASE_MAYBE_OPTIONAL_LINK(Ot::e_TIME) + CASE (Ot::e_CHAR_ARRAY) + CASE (Ot::e_INT_ARRAY) + CASE (Ot::e_INT64_ARRAY) + CASE (Ot::e_DOUBLE_ARRAY) + CASE (Ot::e_STRING_ARRAY) + CASE (Ot::e_DATETIME_ARRAY) + CASE (Ot::e_DATE_ARRAY) + CASE (Ot::e_TIME_ARRAY) + + default: { + BSLS_ASSERT(!"Reached"); } break; + +#undef CASE_MAYBE_OPTIONAL_LINK +#undef CASE } } @@ -342,6 +347,28 @@ int validate(const bsl::vector