. "$BST_LIB" # nlohmann-fifo (fifo_map) is a header-only associative container: a std::map # whose comparator is STATEFUL, stamping each key with an insertion timestamp in # a side table and ordering by that stamp instead of by the key. Iteration # therefore yields INSERTION order. It exists to keep JSON object key order # stable, which is why it is numbered beside 82-nlohmann-json.test (BRINTOS-167, # already in this image): the two are one story, and the reason anyone installs # this one is to hand it to that one as nlohmann::json's object type. # # The port ships ONE FILE -- /usr/include/fifo_map.hpp -- and nothing else. No # archive, no shared object, no cmake package (unlike pegtl), no CLI, and no C++ # compiler on the target. So the honest reading of what a file listing can prove # here is: almost nothing. It cannot distinguish this container from a std::map, # and that distinction IS the package. A std::map substituted underneath would # compile, link, satisfy every existence assertion, and silently reorder every # object key -- which is the precise failure fifo_map exists to prevent, and the # one that would ship undetected. # # Section 3 is therefore the suite: a probe the recipe builds from these very # headers, run once, whose centre is a CONTROL -- the same key sequence pushed # through a real std::map beside it. `P-ORDER` must read as inserted and # `P-MAP-ORDER` must read as sorted. The two agreeing is the whole failure mode # stated as one assertion. # # TWO UPSTREAM DEFECTS ARE PINNED HERE AT THEIR MEASURED VALUES, not at the # values they ought to have -- see section 4. Both were found by this probe, # both reproduce on x86-64 with g++ and exceptions ON, and both are therefore # upstream's at this SRCREV rather than anything about wasm32 or -fno-exceptions. # They are asserted so that a change in either direction -- an upstream fix, or a # new divergence on this arm -- turns this suite red instead of passing silently. # # EXCEPTIONS, because a consumer has to know and the payload does not say: this # arm compiles C++ with -fno-exceptions, and unlike PEGTL, fifo_map carries no # __cpp_exceptions guards at all -- it was not written for the configuration. # Exactly one member is affected. at() forwards to std::map::at, which reports a # missing key by throwing std::out_of_range; under libc++ with exceptions off # that becomes abort(). at() on a PRESENT key is unaffected and is exercised # below. at() on a MISSING key is not a catchable error on this arm and is # deliberately not exercised -- there is no way to observe it that leaves the # suite running. Use find() or count() instead, both of which are exercised here. # # Fork discipline (the lesson 81-cjson paid for): the runner's per-suite bound is # a budget in PROCESS SPAWNS, not in cases. The probe is run ONCE with its whole # output kept in a shell variable, and every assertion below is a fork-free # `case` glob against it. has/hasnot stay local to this suite so a hot helper # cannot change behaviour under the other suites. hdr=/usr/include/fifo_map.hpp PT=/usr/libexec/nlohmann-fifo-tests # has -- $H is the haystack; no fork has() { case "$H" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } # hasnot -- the assertion is that it is ABSENT hasnot() { case "$H" in *"$2"*) bst_notok "$1" "present but must not be: $2" ;; *) bst_ok "$1" ;; esac; } # --------------------------------------------------------------------------- # 1. Payload. One file, one read, then globs -- everything the port installs. require ls nlohmann-fifo/present check nlohmann-fifo/hdr-shipped test -f "$hdr" H=$(cat "$hdr" 2>/dev/null) # The include guard and the namespace, which are what a consumer writes. has nlohmann-fifo/guard '#ifndef NLOHMANN_FIFO_MAP_HPP' has nlohmann-fifo/namespace 'namespace nlohmann' # The two class templates. fifo_map_compare is not an implementation detail a # consumer can ignore: it is the default Compare argument, so it appears in # every explicit instantiation, and nlohmann::json's ObjectType alias names it. has nlohmann-fifo/cls-compare 'class fifo_map_compare' has nlohmann-fifo/cls-map 'class fifo_map' # The declared surface, asserted from the header rather than from a directory # listing. The probe checks the same names compile (P-TYPEDEFS); these check # they are still SPELLED the way a consumer's code spells them. has nlohmann-fifo/t-key 'using key_type = Key;' has nlohmann-fifo/t-mapped 'using mapped_type = T;' has nlohmann-fifo/t-value 'using value_type = std::pair;' has nlohmann-fifo/t-size 'using size_type = std::size_t;' has nlohmann-fifo/t-compare 'using key_compare = Compare;' has nlohmann-fifo/t-alloc 'using allocator_type = Allocator;' has nlohmann-fifo/t-internal 'using internal_map_type = std::map;' has nlohmann-fifo/t-iter 'using iterator = typename internal_map_type::iterator;' has nlohmann-fifo/t-citer 'using const_iterator = typename internal_map_type::const_iterator;' has nlohmann-fifo/t-riter 'using reverse_iterator = typename internal_map_type::reverse_iterator;' # The members the container is used through. has nlohmann-fifo/m-at 'T& at(const Key& key)' has nlohmann-fifo/m-index 'T& operator[](const Key& key)' has nlohmann-fifo/m-insert 'std::pair insert(const value_type& value)' has nlohmann-fifo/m-emplace 'std::pair emplace(Args&& ... args)' has nlohmann-fifo/m-erase-key 'size_type erase(const key_type& key)' has nlohmann-fifo/m-find 'iterator find(const Key& key)' has nlohmann-fifo/m-count 'size_type count(const Key& key) const' has nlohmann-fifo/m-swap 'void swap(fifo_map& other)' has nlohmann-fifo/m-clear 'void clear() noexcept' # THE MECHANISM, in the header's own words. add_key uses unordered_map::insert, # which does NOT overwrite an existing entry -- that one call is why assigning to # a key already present leaves it where it is (P-ORDER overwrite below), and it # is the single line a reimplementation would most plausibly get wrong. has nlohmann-fifo/mech-add 'm_keys->insert({key, m_timestamp++});' has nlohmann-fifo/mech-remove 'm_keys->erase(key);' has nlohmann-fifo/mech-table 'std::unordered_map m_keys;' # No copy-assignment operator is DECLARED -- the defect section 4 pins. Asserted # as an absence so that upstream adding one turns this red and sends a reader to # the two P-ASSIGN cases below. hasnot nlohmann-fifo/no-copy-assign 'fifo_map& operator=(const fifo_map&' # --------------------------------------------------------------------------- # 2. What is NOT shipped, stated so a consumer stops looking. # # The stock recipe installs the header and nothing else, so there is no # find_package(fifo_map) and no pkg-config file. This is a real difference from # the neighbouring C++ ports (pegtl ships a cmake package) and is worth an # assertion rather than a silence: a consumer whose build system expects one # would otherwise discover it as a confusing cmake error. check nlohmann-fifo/no-cmake-pkg test ! -e /usr/lib/cmake/fifo_map check nlohmann-fifo/no-pkgconfig test ! -e /usr/lib/pkgconfig/fifo_map.pc check nlohmann-fifo/no-lib test ! -e /usr/lib/libfifo_map.so # --------------------------------------------------------------------------- # 3. The probe. Run ONCE; every assertion below is a glob on the captured # output. Every expected value was derived on x86-64 by building THIS # probe source against THESE headers, and is compared byte-exactly. check nlohmann-fifo/probe-shipped test -f "$PT/nlohmann-fifo-probe" tmp=${TMPDIR:-/tmp} probe=$tmp/bst-nlfifo-probe.$$ if cp "$PT/nlohmann-fifo-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then H=$("$probe" 2>&1); prc=$? check_eq nlohmann-fifo/probe-rc 0 "$prc" # -- configuration the rest of the port rests on. LONG-BITS pins the arm: this # is wasm32, and it is the one line here that must NOT match the x86-64 # reference, which reads 64. has nlohmann-fifo/cxx11 'P-CXXSTD 201103' has nlohmann-fifo/exceptions-off 'P-EXCEPTIONS 0' has nlohmann-fifo/long-bits 'P-LONG-BITS 32' has nlohmann-fifo/ptr-bits 'P-PTR-BITS 32' has nlohmann-fifo/typedefs 'P-TYPEDEFS 1' # -- THE property, and the control that makes it falsifiable. Three keys # chosen so insertion order and sorted order differ in every position: a # container that sorted would have to print a completely different string, # not a near miss. has nlohmann-fifo/order-insert 'P-ORDER insert = zebra,apple,mango' has nlohmann-fifo/map-control 'P-MAP-ORDER insert = apple,mango,zebra' has nlohmann-fifo/order-differs 'P-DIFFERS 1' # -- it is still a map: lookup is unaffected by the ordering. has nlohmann-fifo/at-zebra 'P-AT zebra = 1' has nlohmann-fifo/at-apple 'P-AT apple = 2' has nlohmann-fifo/size 'P-SIZE 3' has nlohmann-fifo/count-hit 'P-COUNT mango = 1' has nlohmann-fifo/count-miss 'P-COUNT absent = 0' has nlohmann-fifo/find-hit 'P-FIND apple = 1' has nlohmann-fifo/find-miss 'P-FIND absent = 1' # -- assigning an EXISTING key must not move it. This is what a JSON writer # depends on when it updates a value in place, and it is the direct # consequence of unordered_map::insert not overwriting (section 1). has nlohmann-fifo/order-overwrite 'P-ORDER overwrite = zebra,apple,mango' has nlohmann-fifo/at-overwritten 'P-AT zebra-after = 42' # -- erase then re-insert MUST move the key to the END. The case the ticket # names, and the one a "remember the first order" implementation gets # wrong: erase() drops the timestamp, so the next insertion takes a fresh # one. Note the pair -- the intermediate order is asserted too, so a suite # that erased nothing cannot reach the final line by accident. has nlohmann-fifo/erase-rc 'P-ERASE apple = 1' has nlohmann-fifo/order-erased 'P-ORDER erased = zebra,mango' has nlohmann-fifo/order-reinsert 'P-ORDER reinsert = zebra,mango,apple' has nlohmann-fifo/erase-absent 'P-ERASE absent = 0' # -- insert() reports a duplicate rather than reordering; emplace appends; # erase-by-ITERATOR is a different code path from erase(key) and drops the # timestamp BEFORE unlinking the node. has nlohmann-fifo/insert-new 'P-INSERT new = 1' has nlohmann-fifo/insert-dup 'P-INSERT dup = 0' has nlohmann-fifo/insert-value 'P-AT kiwi = 10' has nlohmann-fifo/order-insert2 'P-ORDER after-insert = zebra,mango,apple,kiwi' has nlohmann-fifo/order-emplace 'P-ORDER emplace = zebra,mango,apple,kiwi,fig' has nlohmann-fifo/order-erase-it 'P-ORDER erase-it = zebra,mango,kiwi,fig' # -- reverse iteration is reverse INSERTION order. has nlohmann-fifo/rorder 'P-RORDER = fig,kiwi,mango,zebra' # -- copy CONSTRUCTION carries the order and the timestamp counter, so a key # added to the copy lands after the copied ones rather than colliding. It # is the one of the two copy paths that is correct -- contrast section 4. has nlohmann-fifo/copy-order 'P-COPY order = zebra,mango,kiwi,fig' has nlohmann-fifo/copy-append 'P-COPY appended = zebra,mango,kiwi,fig,date' has nlohmann-fifo/copy-src 'P-COPY src-unchanged = zebra,mango,kiwi,fig' # -- initializer_list construction takes the list's order. has nlohmann-fifo/ilist 'P-ILIST = one,two,three' # -- range erase, and clear() rebuilding the ORDER STATE and not merely the # elements: a key re-added after clear() starts fresh. has nlohmann-fifo/range-erase 'P-RANGE-ERASE = a,d' has nlohmann-fifo/empty-before 'P-EMPTY before = 0' has nlohmann-fifo/empty-after 'P-EMPTY after = 1' has nlohmann-fifo/size-cleared 'P-SIZE cleared = 0' has nlohmann-fifo/order-after-clear 'P-ORDER after-clear = z,y' # -- non-string keys, so the order cannot be a lexicographic accident: # 5,3,9,1 is neither ascending nor descending, and the std::map control # beside it prints the sort. has nlohmann-fifo/int-order 'P-INT-ORDER = 5,3,9,1' has nlohmann-fifo/int-control 'P-INT-MAP-ORDER = 1,3,5,9' # -- lower_bound/upper_bound operate in INSERTION-ORDER space. That is the # surprising consequence of a stateful comparator and it is pinned because # a consumer treating them as key-ordered queries gets nonsense: for key 9, # inserted third of four, upper_bound is 1. has nlohmann-fifo/int-lb 'P-INT-LB 9 = 9' has nlohmann-fifo/int-ub 'P-INT-UB 9 = 1' # -- scale, and the erase/re-insert cycle at scale: 64 keys inserted in a # permutation no sort could reproduce, then every other one erased and # re-added, which must move exactly those to the tail in re-insertion # order. A timestamp counter that wrapped, collided or failed to advance # shows up here and nowhere else. has nlohmann-fifo/big-size 'P-BIG-SIZE 64' has nlohmann-fifo/big-order 'P-BIG-ORDER-OK 1' has nlohmann-fifo/big-tail 'P-BIG-TAIL-OK 1' has nlohmann-fifo/big-size-after 'P-BIG-SIZE-AFTER 64' # ------------------------------------------------------------------------- # 4. TWO UPSTREAM DEFECTS, pinned at measured values. # # fifo_map's comparator holds a RAW POINTER back to the owning object's # m_keys table. Two operations move a comparator between objects without # re-binding that pointer, leaving an object consulting another's table: # # swap() -- exchanges m_map, m_compare and m_keys, pointers included. # copy ASSIGNMENT -- not declared at all, so the compiler generates a # member-wise one that copies the pointer verbatim. # # In both cases the exchanged/copied CONTENTS read back correctly, because # iteration never compares -- so a smoke test that stopped at "the order came # across" would pass. The damage appears on the next insert, and the assertions # below are the broken values: # # swap-append: after s1.swap(s2), adding a key to s1 writes its timestamp # into s2's table, where none of s1's own keys are found, so every # comparison against them answers "smaller" and the key lands at the # FRONT -- 'v,x,y,w' rather than 'x,y,w,v'. # assign-after-src-clear: after a1 = b1, a1 reads b1's table. The append # immediately afterwards looks correct only because that table still holds # the right stamps; clear the source and a1 loses its ordering on the spot # -- 'q,m,n,o' rather than 'm,n,o,q'. A source that merely goes OUT OF # SCOPE gives the same aliasing as undefined behaviour instead of as a # wrong answer, so assignment is not usable on this container at this # SRCREV. The copy CONSTRUCTOR is (copy-append above). # # Reproduced on x86-64 with g++ and exceptions ON, so neither is a wasm32 # defect nor an artifact of -fno-exceptions; both are upstream's, on every arm. # Per docs/conventions/no-public-upstream-reports.md we file nothing against # third-party public repositories, so these are recorded here and in the # BRINTOS-1118 design doc rather than reported to nlohmann/fifo_map. Asserting # the broken values is what makes this suite notice a change in EITHER # direction; an upstream fix should turn these two red and be read as good news. has nlohmann-fifo/swap-contents-s1 'P-SWAP s1 = x,y,w' has nlohmann-fifo/swap-contents-s2 'P-SWAP s2 = p,q' has nlohmann-fifo/swap-append-upstream-bug 'P-SWAP s1-append = v,x,y,w' has nlohmann-fifo/assign-copied 'P-ASSIGN copied = m,n' has nlohmann-fifo/assign-append 'P-ASSIGN appended = m,n,o' has nlohmann-fifo/assign-aliased-upstream-bug 'P-ASSIGN after-src-clear = q,m,n,o' # -- the probe's own tally, and the sentinel that says it ran to the end. A # probe killed halfway prints a prefix of the lines above and would # otherwise contribute only silence. has nlohmann-fifo/probe-failures 'P-FAILURES 0' has nlohmann-fifo/probe-complete 'P-END' else bst_notok nlohmann-fifo/probe-stage "cannot stage $PT/nlohmann-fifo-probe" fi rm -f "$probe"